diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 6e44769892..6267513a07 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -200,6 +200,16 @@ def findNdkBuildFullPath() { return null } +def reactNativeDevServerPort() { + def value = project.getProperties().get("reactNativeDevServerPort") + return value != null ? value : "8081" +} + +def reactNativeInspectorProxyPort() { + def value = project.getProperties().get("reactNativeInspectorProxyPort") + return value != null ? value : reactNativeDevServerPort() +} + def getNdkBuildFullPath() { def ndkBuildFullPath = findNdkBuildFullPath() if (ndkBuildFullPath == null) { @@ -284,6 +294,10 @@ android { buildConfigField("boolean", "IS_INTERNAL_BUILD", "false") buildConfigField("int", "EXOPACKAGE_FLAGS", "0") + + resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort() + resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort() + testApplicationId("com.facebook.react.tests.gradle") testInstrumentationRunner("androidx.test.runner.AndroidJUnitRunner") } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JSBundleLoader.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSBundleLoader.java index 3aff0da69e..9a67632c5c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/JSBundleLoader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSBundleLoader.java @@ -72,7 +72,7 @@ public abstract class JSBundleLoader { delegate.loadScriptFromFile(cachedFileLocation, sourceURL, false); return sourceURL; } catch (Exception e) { - throw DebugServerException.makeGeneric(e.getMessage(), e); + throw DebugServerException.makeGeneric(sourceURL, e.getMessage(), e); } } }; @@ -94,7 +94,7 @@ public abstract class JSBundleLoader { delegate.loadScriptFromDeltaBundle(sourceURL, nativeDeltaClient, false); return sourceURL; } catch (Exception e) { - throw DebugServerException.makeGeneric(e.getMessage(), e); + throw DebugServerException.makeGeneric(sourceURL, e.getMessage(), e); } } }; diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/DebugServerException.java b/ReactAndroid/src/main/java/com/facebook/react/common/DebugServerException.java index bc2423b665..6c84a9bbc6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/DebugServerException.java +++ b/ReactAndroid/src/main/java/com/facebook/react/common/DebugServerException.java @@ -11,6 +11,7 @@ import javax.annotation.Nullable; import java.io.IOException; +import android.net.Uri; import android.text.TextUtils; import com.facebook.common.logging.FLog; @@ -28,15 +29,19 @@ public class DebugServerException extends RuntimeException { "\u2022 Ensure that the packager server is running\n" + "\u2022 Ensure that your device/emulator is connected to your machine and has USB debugging enabled - run 'adb devices' to see a list of connected devices\n" + "\u2022 Ensure Airplane Mode is disabled\n" + - "\u2022 If you're on a physical device connected to the same machine, run 'adb reverse tcp:8081 tcp:8081' to forward requests from your device\n" + - "\u2022 If your device is on the same Wi-Fi network, set 'Debug server host & port for device' in 'Dev settings' to your machine's IP address and the port of the local dev server - e.g. 10.0.1.1:8081\n\n"; + "\u2022 If you're on a physical device connected to the same machine, run 'adb reverse tcp: tcp:' to forward requests from your device\n" + + "\u2022 If your device is on the same Wi-Fi network, set 'Debug server host & port for device' in 'Dev settings' to your machine's IP address and the port of the local dev server - e.g. 10.0.1.1:\n\n"; - public static DebugServerException makeGeneric(String reason, Throwable t) { - return makeGeneric(reason, "", t); + public static DebugServerException makeGeneric(String url, String reason, Throwable t) { + return makeGeneric(url, reason, "", t); } - public static DebugServerException makeGeneric(String reason, String extra, Throwable t) { - return new DebugServerException(reason + GENERIC_ERROR_MESSAGE + extra, t); + public static DebugServerException makeGeneric(String url, String reason, String extra, Throwable t) { + Uri uri = Uri.parse(url); + + String message = GENERIC_ERROR_MESSAGE.replace("", String.valueOf(uri.getPort())); + + return new DebugServerException(reason + message + extra, t); } private DebugServerException(String description, String fileName, int lineNumber, int column) { @@ -56,7 +61,7 @@ public class DebugServerException extends RuntimeException { * @param str json string returned by the debug server * @return A DebugServerException or null if the string is not of proper form. */ - @Nullable public static DebugServerException parse(String str) { + @Nullable public static DebugServerException parse(String url, String str) { if (TextUtils.isEmpty(str)) { return null; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java index ba798c1416..e7a6fdb0cf 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java @@ -142,10 +142,12 @@ public class BundleDownloader { } mDownloadBundleFromURLCall = null; + String url = call.request().url().toString(); + callback.onFailure( - DebugServerException.makeGeneric( + DebugServerException.makeGeneric(url, "Could not connect to development server.", - "URL: " + call.request().url().toString(), + "URL: " + url, e)); } @@ -284,7 +286,7 @@ public class BundleDownloader { // Check for server errors. If the server error has the expected form, fail with more info. if (statusCode != 200) { String bodyString = body.readUtf8(); - DebugServerException debugServerException = DebugServerException.parse(bodyString); + DebugServerException debugServerException = DebugServerException.parse(url, bodyString); if (debugServerException != null) { callback.onFailure(debugServerException); } else { diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java index f7d9748c96..e9978bb92e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java @@ -8,6 +8,7 @@ package com.facebook.react.devsupport; import android.content.Context; +import android.content.res.Resources; import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; @@ -261,7 +262,7 @@ public class DevServerHelper { public boolean doSync() { try { - String attachToNuclideUrl = getInspectorAttachUrl(title); + String attachToNuclideUrl = getInspectorAttachUrl(context, title); OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(attachToNuclideUrl).build(); client.newCall(request).execute(); @@ -367,11 +368,11 @@ public class DevServerHelper { mPackageName); } - private String getInspectorAttachUrl(String title) { + private String getInspectorAttachUrl(Context context, String title) { return String.format( Locale.US, "http://%s/nuclide/attach-debugger-nuclide?title=%s&app=%s&device=%s", - AndroidInfoHelpers.getServerHost(), + AndroidInfoHelpers.getServerHost(context), title, mPackageName, AndroidInfoHelpers.getFriendlyDeviceName()); diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.java b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.java index c336a7b5d6..3659abaccc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.java @@ -8,12 +8,14 @@ package com.facebook.react.modules.systeminfo; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.Locale; +import android.content.Context; +import android.content.res.Resources; import android.os.Build; import com.facebook.common.logging.FLog; +import com.facebook.react.R; public class AndroidInfoHelpers { @@ -23,9 +25,6 @@ public class AndroidInfoHelpers { public static final String METRO_HOST_PROP_NAME = "metro.host"; - private static final int DEBUG_SERVER_HOST_PORT = 8081; - private static final int INSPECTOR_PROXY_PORT = 8081; - private static final String TAG = AndroidInfoHelpers.class.getSimpleName(); private static boolean isRunningOnGenymotion() { @@ -36,12 +35,24 @@ public class AndroidInfoHelpers { return Build.FINGERPRINT.contains("generic"); } - public static String getServerHost() { - return getServerIpAddress(DEBUG_SERVER_HOST_PORT); + public static String getServerHost(Integer port) { + return getServerIpAddress(port); } - public static String getInspectorProxyHost() { - return getServerIpAddress(INSPECTOR_PROXY_PORT); + public static String getServerHost(Context context) { + return getServerIpAddress(getDevServerPort(context)); + } + + public static String getAdbReverseTcpCommand(Integer port) { + return "adb reverse tcp:" + port + " tcp:" + port; + } + + public static String getAdbReverseTcpCommand(Context context) { + return getAdbReverseTcpCommand(getDevServerPort(context)); + } + + public static String getInspectorProxyHost(Context context) { + return getServerIpAddress(getInspectorProxyPort(context)); } // WARNING(festevezga): This RN helper method has been copied to another FB-only target. Any changes should be applied to both. @@ -54,6 +65,16 @@ public class AndroidInfoHelpers { } } + private static Integer getDevServerPort(Context context) { + Resources resources = context.getResources(); + return resources.getInteger(R.integer.react_native_dev_server_port); + } + + private static Integer getInspectorProxyPort(Context context) { + Resources resources = context.getResources(); + return resources.getInteger(R.integer.react_native_dev_server_port); + } + private static String getServerIpAddress(int port) { // Since genymotion runs in vbox it use different hostname to refer to adb host. // We detect whether app runs on genymotion and replace js bundle server hostname accordingly diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java index 391dedccd8..cf5ca4036c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoModule.java @@ -9,10 +9,13 @@ package com.facebook.react.modules.systeminfo; import android.annotation.SuppressLint; import android.app.UiModeManager; +import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; import android.os.Build; import android.provider.Settings.Secure; +import com.facebook.react.R; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.common.build.ReactBuildConfig; @@ -35,9 +38,7 @@ public class AndroidInfoModule extends ReactContextBaseJavaModule { public static final String NAME = "PlatformConstants"; private static final String IS_TESTING = "IS_TESTING"; - public AndroidInfoModule(ReactApplicationContext reactContext) { - super(reactContext); - } + public AndroidInfoModule(ReactApplicationContext reactContext) { super(reactContext); } /** * See: https://developer.android.com/reference/android/app/UiModeManager.html#getCurrentModeType() @@ -74,7 +75,7 @@ public class AndroidInfoModule extends ReactContextBaseJavaModule { constants.put("Fingerprint", Build.FINGERPRINT); constants.put("Model", Build.MODEL); if (ReactBuildConfig.DEBUG) { - constants.put("ServerHost", AndroidInfoHelpers.getServerHost()); + constants.put("ServerHost", getServerHost()); } constants.put("isTesting", "true".equals(System.getProperty(IS_TESTING)) || isRunningScreenshotTest()); @@ -96,4 +97,12 @@ public class AndroidInfoModule extends ReactContextBaseJavaModule { return false; } } + + private String getServerHost() { + Resources resources = getReactApplicationContext().getApplicationContext().getResources(); + + Integer devServerPort = resources.getInteger(R.integer.react_native_dev_server_port); + + return AndroidInfoHelpers.getServerHost(devServerPort); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK index 396d6c574e..433ecaf808 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/BUCK @@ -14,6 +14,7 @@ rn_android_library( react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/module/annotations:annotations"), + react_native_target("res:systeminfo"), ], exported_deps = [ ":systeminfo-moduleless", @@ -29,8 +30,10 @@ rn_android_library( "PUBLIC", ], deps = [ + react_native_target("java/com/facebook/react/common:common"), react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), react_native_dep("third-party/java/infer-annotations:infer-annotations"), react_native_dep("third-party/java/jsr-305:jsr-305"), + react_native_target("res:systeminfo"), ], ) diff --git a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.java b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.java index 98ac8cff7f..a154553f0d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.java +++ b/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.java @@ -7,13 +7,13 @@ package com.facebook.react.packagerconnection; -import javax.annotation.Nullable; - import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.text.TextUtils; +import javax.annotation.Nullable; + import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.modules.systeminfo.AndroidInfoHelpers; @@ -24,10 +24,12 @@ public class PackagerConnectionSettings { private final SharedPreferences mPreferences; private final String mPackageName; + private final Context mAppContext; public PackagerConnectionSettings(Context applicationContext) { mPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext); mPackageName = applicationContext.getPackageName(); + mAppContext = applicationContext; } public String getDebugServerHost() { @@ -39,12 +41,12 @@ public class PackagerConnectionSettings { return Assertions.assertNotNull(hostFromSettings); } - String host = AndroidInfoHelpers.getServerHost(); + String host = AndroidInfoHelpers.getServerHost(mAppContext); if (host.equals(AndroidInfoHelpers.DEVICE_LOCALHOST)) { FLog.w( TAG, - "You seem to be running on device. Run 'adb reverse tcp:8081 tcp:8081' " + + "You seem to be running on device. Run '" + AndroidInfoHelpers.getAdbReverseTcpCommand(mAppContext) + "' " + "to forward the debug server's port to the device."); } @@ -52,7 +54,7 @@ public class PackagerConnectionSettings { } public String getInspectorServerHost() { - return AndroidInfoHelpers.getInspectorProxyHost(); + return AndroidInfoHelpers.getInspectorProxyHost(mAppContext); } public @Nullable String getPackageName() { diff --git a/ReactAndroid/src/main/res/BUCK b/ReactAndroid/src/main/res/BUCK index afb0be006d..9d24c62248 100644 --- a/ReactAndroid/src/main/res/BUCK +++ b/ReactAndroid/src/main/res/BUCK @@ -36,4 +36,13 @@ rn_android_resource( ], ) +rn_android_resource( + name = "systeminfo", + package = "com.facebook.react", + res = "systeminfo", + visibility = [ + "PUBLIC", + ], +) + # New resource directories must be added to react-native-github/ReactAndroid/build.gradle diff --git a/ReactAndroid/src/main/res/systeminfo/values/values.xml b/ReactAndroid/src/main/res/systeminfo/values/values.xml new file mode 100644 index 0000000000..7d52389be8 --- /dev/null +++ b/ReactAndroid/src/main/res/systeminfo/values/values.xml @@ -0,0 +1,5 @@ + + + 8081 + @integer/react_native_dev_server_port + diff --git a/react.gradle b/react.gradle index 07fd9e3f25..9b45a8899c 100644 --- a/react.gradle +++ b/react.gradle @@ -15,6 +15,22 @@ def reactRoot = file(config.root ?: "../../") def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"] def bundleConfig = config.bundleConfig ? "${reactRoot}/${config.bundleConfig}" : null ; +def reactNativeDevServerPort() { + def value = project.getProperties().get("reactNativeDevServerPort") + return value != null ? value : "8081" +} + +def reactNativeInspectorProxyPort() { + def value = project.getProperties().get("reactNativeInspectorProxyPort") + return value != null ? value : reactNativeDevServerPort() +} + +android { + buildTypes.all { + resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort() + resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort() + } +} afterEvaluate { def isAndroidLibrary = plugins.hasPlugin("com.android.library")