Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow client-side device identifiers in inspector proxy #991

Conversation

byCedric
Copy link
Contributor

@byCedric byCedric commented May 27, 2023

Summary

Fixes #985

This allows devices connecting to the inspector proxy to provide a client-side unique string identifier. With this, we can pass along a unique identifier that doesn't change when reconnecting, e.g. after a hard crash.

It makes the overall debugging experience way more stable since you don't have to restart the debugger in between crashes or full restarts. Allowing users to keep the debugger open also lets the debugger "remember" certain things, such as set breakpoints.

⚠️ When the client doesn't specify this unique identifier, it still falls back to the incremental identifier (the fallbackDeviceId in this PR)

When a collision occurs, the old device's connection is closed. But, if both the device and app names are equal to the new device connection, the debugger connection is kept open.

Next steps

If this is landed, we still need changes in React Native (the &device=... query param). I have no strong opinions on what identifier is used, but it should follow these rules:

  • Should be unique per device or emulator/simulator (and thus, also per platform)
  • Must not change when restarting the app
  • Must be a string that's URL safe

On Android, I had good success using the Secure.ANDROID_ID. It may change after a factory reset, which doesn't matter for our use case.

Test plan

  • Create a new project, and enable building from source on both Android & iOS
  • android: edit this file in node_modules/react-native and add a hardcoded &device=testingandroid query param.
  • ios: edit this file in node_modules/react-naive and add a hardcoded &device=testingios query param.
  • Connect the debugger to the running app
  • Force close the app, which should cause a "reconnect" warning in the debugger
  • Open the app again, and press "reconnect" in the debugger
  • Due to the stable identifiers, the URL won't change and the above scenario should work fine

Also test without these &device=... query param since that should work as it does now (with incremental identifiers).

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label May 27, 2023
@byCedric byCedric force-pushed the @bycedric/inspector-proxy/allow-device-identifiers branch from 1de8117 to f2ca178 Compare May 27, 2023 20:05
@huntie huntie self-assigned this May 30, 2023

const oldDevice = this._devices.get(deviceId);
if (oldDevice) {
// Keep the debugger connection alive when disconnecting the device, if possible
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the new Device created below somehow inherit _debuggerConnection from oldDevice?

Also, _debuggerConnection is a private variable (by naming convention) and ideally shouldn't be accessed outside of the Device class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the new Device created below somehow inherit _debuggerConnection from oldDevice?

It does, but we might need to add some logic when swapping out the Device instances. Basically, in my tests I found that the open debugger automatically triggered the Device.handleDebuggerConnection method (background reconnection). This sets the debugger connection to the new Device instance.

I think an ideal scenario is to just invoke that method directly during the initialization.

Originally, I had to unset the debugger connection in order to avoid the device's socket close event handler.

Copy link
Contributor Author

@byCedric byCedric Jun 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, _debuggerConnection is a private variable (by naming convention) and ideally shouldn't be accessed outside of the Device class

One thing we can do is move the shutdown logic into a handler on the device class. With that, we don't need to access _debuggerConnection, _name, and _app outside the class. Does that make sense to you? @voideanvalue

Copy link
Member

@huntie huntie Jun 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup makes sense. I think feel free to modify the Device class (as it's private within this package) to accomplish the new logic needed in a well-named method(s).

@byCedric byCedric force-pushed the @bycedric/inspector-proxy/allow-device-identifiers branch from adb4dec to 9bbf992 Compare June 2, 2023 11:14
packages/metro-inspector-proxy/src/Device.js Outdated Show resolved Hide resolved
packages/metro-inspector-proxy/src/InspectorProxy.js Outdated Show resolved Hide resolved
@huntie
Copy link
Member

huntie commented Jun 2, 2023

Thanks Cedric! LGTM with small nits 🙂. Will import and it'll receive a final review.

The criteria for a device/session ID sound good — a stable identifier that is unique per device and app. If you need, I'm happy to handle the next steps on the React Native side.

@byCedric
Copy link
Contributor Author

byCedric commented Jun 2, 2023

If you need, I'm happy to handle the next steps on the React Native side.

That would be great 🙏

@motiz88
Copy link
Contributor

motiz88 commented Jun 5, 2023

The criteria for a device/session ID sound good — a stable identifier that is unique per device and app.

To nitpick slightly - a device ID and session ID don't sound like the same thing. It's important to consider what happens if we have multiple devices, multiple apps on each device, and possibly even multiple instances of React Native in each app (and in the future, multiple JavaScript contexts e.g. Web Workers in each instance of React Native) - all of which could reasonably be connected to one instance of Metro.

This PR looks good, but eventually we should figure out all the different entities and their lifetimes, and flesh out what types of IDs (and persistence/stability) we need at each level.

@facebook-github-bot
Copy link
Contributor

@huntie has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@byCedric
Copy link
Contributor Author

byCedric commented Jun 7, 2023

@motiz88 fully agree, might also be good to check with Hermes if we need to reinitialize certain events. E.g. does Hermes enable debugging only based on the Debugger.enable event?

With their input, I'm sure we can come up with a great overview of each component, its life cycle, and what happens when non-happy-path things occur.

@facebook-github-bot
Copy link
Contributor

@huntie has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@huntie merged this pull request in c6a94bc.

byCedric added a commit to expo/expo that referenced this pull request Jun 16, 2023
… when restarting app (#22742)

# Why

After using the debugger for some time, I noticed a really annoying
behavior when the app crashes or is restarted manually. See
facebook/metro#985 for more info.

# How

This adds support for client-side unique device identifiers, such as
"installation ids" or "device ids". Similarly to future support within
Metro: facebook/metro#991

# Test Plan

See updated tests, and the test plan at
facebook/metro#991

> - Create a new project, and enable building from source on both
Android & iOS
> - **android**: edit [this
file](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java#L282-L289)
in `node_modules/react-native` and add a hardcoded
`&device=testingandroid` query param.
> - **ios**: edit [this
file](https://github.com/facebook/react-native/blob/main/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm#L43-L53)
in `node_modules/react-naive` and add a hardcoded `&device=testingios`
query param.
> - Connect the debugger to the running app
> - Force close the app, which should cause a "reconnect" warning in the
debugger
> - Open the app again, and press "reconnect" in the debugger
> - _Due to the stable identifiers, the URL won't change and the above
scenario should work fine_

# Checklist

<!--
Please check the appropriate items below if they apply to your diff.
This is required for changes to Expo modules.
-->

- [ ] Documentation is up to date to reflect these changes (eg:
https://docs.expo.dev and README.md).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
@byCedric byCedric deleted the @bycedric/inspector-proxy/allow-device-identifiers branch August 14, 2023 13:53
blakef pushed a commit to blakef/react-native that referenced this pull request Oct 19, 2023
Summary:
Building on byCedric's approach in facebook/metro#991, adds support for passing a `device=...` argument to `/open-debugger` for more precise targeting.

Changelog: [Internal]

 ---

## Note on what "device" means in this context

In `dev-middleware` / `inspector-proxy`, "device" is something of a misnomer. It refers to a *logical device* containing one or more *pages*. In React Native, each app process forms its own logical device in which individual VMs register themselves as pages. An instance of `inspector-proxy` connects one or more *debuggers* (frontends) to one or more logical devices (one frontend to one page on one device).

The intent of the logical device ID is to help with target discovery and especially *re*discovery - to reduce the number of times users need to explicitly close and restart the debugger frontend (e.g. after an app crash).

If provided, the logical device ID:
1. SHOULD be stable for the current combination of physical device (or emulator instance) and app.
2. SHOULD be stable across installs/launches of the same app on the same device (or emulator instance), though it MAY be user-resettable (so as to not require any special privacy permissions).
3. MUST be unique across different apps on the same physical device (or emulator).
4. MUST be unique across physical devices (or emulators).
5. MUST be unique for each concurrent *instance* of the same app on the same physical device (or emulator).

NOTE: The uniqueness requirements are stronger (MUST) than the stability requirements (SHOULD). In particular, on platforms that allow multiple instances of the same app to run concurrently, requirements 1 and/or 2 MAY be violated in order to meet requirement 5. This will be relevant, for example, on desktop platforms.

In an upcoming diff, we will pass device IDs meeting these criteria from both iOS and Android.

Reviewed By: huntie, blakef

Differential Revision: D49954920

fbshipit-source-id: 4028e717f2f0ab1fc00f6db0ad154a85d8361c17
blakef pushed a commit to blakef/react-native that referenced this pull request Oct 20, 2023
Summary:

Building on byCedric's approach in facebook/metro#991, adds support for passing a `device=...` argument to `/open-debugger` for more precise targeting.

Changelog: [Internal]

---

## Note on what "device" means in this context

In `dev-middleware` / `inspector-proxy`, "device" is something of a misnomer. It refers to a *logical device* containing one or more *pages*. In React Native, each app process forms its own logical device in which individual VMs register themselves as pages. An instance of `inspector-proxy` connects one or more *debuggers* (frontends) to one or more logical devices (one frontend to one page on one device).

The intent of the logical device ID is to help with target discovery and especially *re*discovery - to reduce the number of times users need to explicitly close and restart the debugger frontend (e.g. after an app crash).

If provided, the logical device ID:
1. SHOULD be stable for the current combination of physical device (or emulator instance) and app.
2. SHOULD be stable across installs/launches of the same app on the same device (or emulator instance), though it MAY be user-resettable (so as to not require any special privacy permissions).
3. MUST be unique across different apps on the same physical device (or emulator).
4. MUST be unique across physical devices (or emulators).
5. MUST be unique for each concurrent *instance* of the same app on the same physical device (or emulator).

NOTE: The uniqueness requirements are stronger (MUST) than the stability requirements (SHOULD). In particular, on platforms that allow multiple instances of the same app to run concurrently, requirements 1 and/or 2 MAY be violated in order to meet requirement 5. This will be relevant, for example, on desktop platforms.

In an upcoming diff, we will pass device IDs meeting these criteria from both iOS and Android.

Reviewed By: huntie, blakef

Differential Revision: D49954920
motiz88 added a commit to motiz88/react-native that referenced this pull request Oct 23, 2023
Summary:
Building on byCedric's approach in facebook/metro#991, and on D49954920, this diff passes stable, unique *logical device IDs* to the debugger connection infrastructure from Android and iOS.

See D49954920 for the precise stability and uniqueness requirements that these IDs meet.

Changelog:

[Changed][General] - Automatically reconnect to an existing debugger session on relaunching the app

Reviewed By: huntie

Differential Revision: D49954919

fbshipit-source-id: 5796209efd0104f482d2a98c2616a163ea67ea61
motiz88 added a commit to motiz88/react-native that referenced this pull request Oct 23, 2023
Summary:
Pull Request resolved: facebook#41080

Building on byCedric's approach in facebook/metro#991, adds support for passing a `device=...` argument to `/open-debugger` for more precise targeting.

Changelog: [Internal]

 ---

## Note on what "device" means in this context

In `dev-middleware` / `inspector-proxy`, "device" is something of a misnomer. It refers to a *logical device* containing one or more *pages*. In React Native, each app process forms its own logical device in which individual VMs register themselves as pages. An instance of `inspector-proxy` connects one or more *debuggers* (frontends) to one or more logical devices (one frontend to one page on one device).

The intent of the logical device ID is to help with target discovery and especially *re*discovery - to reduce the number of times users need to explicitly close and restart the debugger frontend (e.g. after an app crash).

If provided, the logical device ID:
1. SHOULD be stable for the current combination of physical device (or emulator instance) and app.
2. SHOULD be stable across installs/launches of the same app on the same device (or emulator instance), though it MAY be user-resettable (so as to not require any special privacy permissions).
3. MUST be unique across different apps on the same physical device (or emulator).
4. MUST be unique across physical devices (or emulators).
5. MUST be unique for each concurrent *instance* of the same app on the same physical device (or emulator).

NOTE: The uniqueness requirements are stronger (MUST) than the stability requirements (SHOULD). In particular, on platforms that allow multiple instances of the same app to run concurrently, requirements 1 and/or 2 MAY be violated in order to meet requirement 5. This will be relevant, for example, on desktop platforms.

In an upcoming diff, we will pass device IDs meeting these criteria from both iOS and Android.

Reviewed By: huntie, blakef

Differential Revision: D49954920

fbshipit-source-id: b37b90bea5b285a100b55cf44d6d21b4e7bd3e79
motiz88 added a commit to motiz88/react-native that referenced this pull request Oct 23, 2023
Summary:


Building on byCedric's approach in facebook/metro#991, adds support for passing a `device=...` argument to `/open-debugger` for more precise targeting.

Changelog: [Internal]

---

## Note on what "device" means in this context

In `dev-middleware` / `inspector-proxy`, "device" is something of a misnomer. It refers to a *logical device* containing one or more *pages*. In React Native, each app process forms its own logical device in which individual VMs register themselves as pages. An instance of `inspector-proxy` connects one or more *debuggers* (frontends) to one or more logical devices (one frontend to one page on one device).

The intent of the logical device ID is to help with target discovery and especially *re*discovery - to reduce the number of times users need to explicitly close and restart the debugger frontend (e.g. after an app crash).

If provided, the logical device ID:
1. SHOULD be stable for the current combination of physical device (or emulator instance) and app.
2. SHOULD be stable across installs/launches of the same app on the same device (or emulator instance), though it MAY be user-resettable (so as to not require any special privacy permissions).
3. MUST be unique across different apps on the same physical device (or emulator).
4. MUST be unique across physical devices (or emulators).
5. MUST be unique for each concurrent *instance* of the same app on the same physical device (or emulator).

NOTE: The uniqueness requirements are stronger (MUST) than the stability requirements (SHOULD). In particular, on platforms that allow multiple instances of the same app to run concurrently, requirements 1 and/or 2 MAY be violated in order to meet requirement 5. This will be relevant, for example, on desktop platforms.

In an upcoming diff, we will pass device IDs meeting these criteria from both iOS and Android.

Reviewed By: huntie, blakef

Differential Revision: D49954920
motiz88 added a commit to motiz88/react-native that referenced this pull request Oct 23, 2023
Summary:


Building on byCedric's approach in facebook/metro#991, adds support for passing a `device=...` argument to `/open-debugger` for more precise targeting.

Changelog: [Internal]

---

## Note on what "device" means in this context

In `dev-middleware` / `inspector-proxy`, "device" is something of a misnomer. It refers to a *logical device* containing one or more *pages*. In React Native, each app process forms its own logical device in which individual VMs register themselves as pages. An instance of `inspector-proxy` connects one or more *debuggers* (frontends) to one or more logical devices (one frontend to one page on one device).

The intent of the logical device ID is to help with target discovery and especially *re*discovery - to reduce the number of times users need to explicitly close and restart the debugger frontend (e.g. after an app crash).

If provided, the logical device ID:
1. SHOULD be stable for the current combination of physical device (or emulator instance) and app.
2. SHOULD be stable across installs/launches of the same app on the same device (or emulator instance), though it MAY be user-resettable (so as to not require any special privacy permissions).
3. MUST be unique across different apps on the same physical device (or emulator).
4. MUST be unique across physical devices (or emulators).
5. MUST be unique for each concurrent *instance* of the same app on the same physical device (or emulator).

NOTE: The uniqueness requirements are stronger (MUST) than the stability requirements (SHOULD). In particular, on platforms that allow multiple instances of the same app to run concurrently, requirements 1 and/or 2 MAY be violated in order to meet requirement 5. This will be relevant, for example, on desktop platforms.

In an upcoming diff, we will pass device IDs meeting these criteria from both iOS and Android.

Reviewed By: huntie, blakef

Differential Revision: D49954920
motiz88 added a commit to motiz88/react-native that referenced this pull request Oct 23, 2023
…book#41152)

Summary:

Building on byCedric's approach in facebook/metro#991, and on D49954920, this diff passes stable, unique *logical device IDs* to the debugger connection infrastructure from Android and iOS.

See D49954920 for the precise stability and uniqueness requirements that these IDs meet.

Changelog:

[Changed][General] - Automatically reconnect to an existing debugger session on relaunching the app

Reviewed By: huntie

Differential Revision: D49954919
facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Oct 23, 2023
Summary:
Pull Request resolved: #41154

Pull Request resolved: #41080

Building on byCedric's approach in facebook/metro#991, adds support for passing a `device=...` argument to `/open-debugger` for more precise targeting.

Changelog: [Internal]

 ---

## Note on what "device" means in this context

In `dev-middleware` / `inspector-proxy`, "device" is something of a misnomer. It refers to a *logical device* containing one or more *pages*. In React Native, each app process forms its own logical device in which individual VMs register themselves as pages. An instance of `inspector-proxy` connects one or more *debuggers* (frontends) to one or more logical devices (one frontend to one page on one device).

The intent of the logical device ID is to help with target discovery and especially *re*discovery - to reduce the number of times users need to explicitly close and restart the debugger frontend (e.g. after an app crash).

If provided, the logical device ID:
1. SHOULD be stable for the current combination of physical device (or emulator instance) and app.
2. SHOULD be stable across installs/launches of the same app on the same device (or emulator instance), though it MAY be user-resettable (so as to not require any special privacy permissions).
3. MUST be unique across different apps on the same physical device (or emulator).
4. MUST be unique across physical devices (or emulators).
5. MUST be unique for each concurrent *instance* of the same app on the same physical device (or emulator).

NOTE: The uniqueness requirements are stronger (MUST) than the stability requirements (SHOULD). In particular, on platforms that allow multiple instances of the same app to run concurrently, requirements 1 and/or 2 MAY be violated in order to meet requirement 5. This will be relevant, for example, on desktop platforms.

In an upcoming diff, we will pass device IDs meeting these criteria from both iOS and Android.

Reviewed By: huntie, blakef

Differential Revision: D49954920

fbshipit-source-id: 45f2b50765dece34cbb93fa32abcdf3b0522391c
facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Oct 23, 2023
Summary:
Pull Request resolved: #41152

Building on byCedric's approach in facebook/metro#991, and on D49954920, this diff passes stable, unique *logical device IDs* to the debugger connection infrastructure from Android and iOS.

See D49954920 for the precise stability and uniqueness requirements that these IDs meet.

Changelog:

[Changed][General] - Automatically reconnect to an existing debugger session on relaunching the app

Reviewed By: huntie

Differential Revision: D49954919

fbshipit-source-id: d4d918f0cbfd9df426e888845817e00410efb9d3
Othinn pushed a commit to Othinn/react-native that referenced this pull request Oct 30, 2023
Summary:
Pull Request resolved: facebook#41154

Pull Request resolved: facebook#41080

Building on byCedric's approach in facebook/metro#991, adds support for passing a `device=...` argument to `/open-debugger` for more precise targeting.

Changelog: [Internal]

 ---

## Note on what "device" means in this context

In `dev-middleware` / `inspector-proxy`, "device" is something of a misnomer. It refers to a *logical device* containing one or more *pages*. In React Native, each app process forms its own logical device in which individual VMs register themselves as pages. An instance of `inspector-proxy` connects one or more *debuggers* (frontends) to one or more logical devices (one frontend to one page on one device).

The intent of the logical device ID is to help with target discovery and especially *re*discovery - to reduce the number of times users need to explicitly close and restart the debugger frontend (e.g. after an app crash).

If provided, the logical device ID:
1. SHOULD be stable for the current combination of physical device (or emulator instance) and app.
2. SHOULD be stable across installs/launches of the same app on the same device (or emulator instance), though it MAY be user-resettable (so as to not require any special privacy permissions).
3. MUST be unique across different apps on the same physical device (or emulator).
4. MUST be unique across physical devices (or emulators).
5. MUST be unique for each concurrent *instance* of the same app on the same physical device (or emulator).

NOTE: The uniqueness requirements are stronger (MUST) than the stability requirements (SHOULD). In particular, on platforms that allow multiple instances of the same app to run concurrently, requirements 1 and/or 2 MAY be violated in order to meet requirement 5. This will be relevant, for example, on desktop platforms.

In an upcoming diff, we will pass device IDs meeting these criteria from both iOS and Android.

Reviewed By: huntie, blakef

Differential Revision: D49954920

fbshipit-source-id: 45f2b50765dece34cbb93fa32abcdf3b0522391c
Othinn pushed a commit to Othinn/react-native that referenced this pull request Oct 30, 2023
…book#41152)

Summary:
Pull Request resolved: facebook#41152

Building on byCedric's approach in facebook/metro#991, and on D49954920, this diff passes stable, unique *logical device IDs* to the debugger connection infrastructure from Android and iOS.

See D49954920 for the precise stability and uniqueness requirements that these IDs meet.

Changelog:

[Changed][General] - Automatically reconnect to an existing debugger session on relaunching the app

Reviewed By: huntie

Differential Revision: D49954919

fbshipit-source-id: d4d918f0cbfd9df426e888845817e00410efb9d3
douglowder pushed a commit to react-native-tvos/react-native-tvos that referenced this pull request Nov 30, 2023
Summary:
Pull Request resolved: facebook/react-native#41152

Building on byCedric's approach in facebook/metro#991, and on D49954920, this diff passes stable, unique *logical device IDs* to the debugger connection infrastructure from Android and iOS.

See D49954920 for the precise stability and uniqueness requirements that these IDs meet.

Changelog:

[Changed][General] - Automatically reconnect to an existing debugger session on relaunching the app

Reviewed By: huntie

Differential Revision: D49954919

fbshipit-source-id: d4d918f0cbfd9df426e888845817e00410efb9d3
huntie pushed a commit to facebook/react-native that referenced this pull request Dec 19, 2023
Summary:
Pull Request resolved: #41152

Building on byCedric's approach in facebook/metro#991, and on D49954920, this diff passes stable, unique *logical device IDs* to the debugger connection infrastructure from Android and iOS.

See D49954920 for the precise stability and uniqueness requirements that these IDs meet.

Changelog:

[Changed][General] - Automatically reconnect to an existing debugger session on relaunching the app

Reviewed By: huntie

Differential Revision: D49954919

fbshipit-source-id: d4d918f0cbfd9df426e888845817e00410efb9d3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve inspector proxy device identifier
5 participants