mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
feat(android): force 3-button nav during runs to prevent app drift
On gesture navigation a fuzzer swipe can trigger swipe-up-home or edge-back and fling the app off screen. Device-prep now switches to 3-button navigation for the run (no edge gestures; the nav bar's buttons are systemui-owned and already excluded from action candidates) and restores the original navigation mode when the run ends. Best effort: leaves nav untouched if the overlay command is unavailable.
This commit is contained in:
1 parent
229f015281
commit
542bf11a02
3 files changed
+112
No files matched your search
@@ -136,6 +136,82 @@ func ReinstallApp(ctx context.Context, serial, bundleID, apkPath string, stdout
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const threeButtonNavOverlay = "com.android.internal.systemui.navbar.threebutton"
|
||||||
|
|
||||||
|
// navModeOverlays are the system navigation-mode overlays. Only one is active at
|
||||||
|
// a time; the active one is restored after the run.
|
||||||
|
var navModeOverlays = []string{
|
||||||
|
"com.android.internal.systemui.navbar.gestural",
|
||||||
|
threeButtonNavOverlay,
|
||||||
|
"com.android.internal.systemui.navbar.twobutton",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceThreeButtonNav switches the device to 3-button navigation for the run, so
|
||||||
|
// the fuzzer's swipes cannot trigger the gesture-nav home/back actions and fling
|
||||||
|
// the app off screen (the nav bar's own buttons are systemui-owned and already
|
||||||
|
// dropped from action candidates). It returns a function that restores the
|
||||||
|
// original navigation mode. Best effort: on any failure it leaves navigation
|
||||||
|
// untouched and returns a no-op restore.
|
||||||
|
func ForceThreeButtonNav(ctx context.Context, serial string, stdout io.Writer) func() {
|
||||||
|
adb, err := AdbBinary()
|
||||||
|
if err != nil {
|
||||||
|
return func() {}
|
||||||
|
}
|
||||||
|
original := enabledNavOverlay(ctx, adb, serial)
|
||||||
|
if err := navOverlayCommand(ctx, adb, serial, threeButtonNavOverlay).Run(); err != nil {
|
||||||
|
fmt.Fprintf(stdout, "device prep: skipping 3-button nav (%v)\n", err)
|
||||||
|
return func() {}
|
||||||
|
}
|
||||||
|
if original == "" || original == threeButtonNavOverlay {
|
||||||
|
return func() {}
|
||||||
|
}
|
||||||
|
return func() {
|
||||||
|
if err := navOverlayCommand(context.Background(), adb, serial, original).Run(); err != nil {
|
||||||
|
fmt.Fprintf(stdout, "device prep: could not restore nav mode %s (%v)\n", original, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enabledNavOverlay returns the currently active navigation-mode overlay, or ""
|
||||||
|
// when it cannot be determined.
|
||||||
|
func enabledNavOverlay(ctx context.Context, adb, serial string) string {
|
||||||
|
args := []string{}
|
||||||
|
if serial != "" {
|
||||||
|
args = append(args, "-s", serial)
|
||||||
|
}
|
||||||
|
args = append(args, "shell", "cmd", "overlay", "list")
|
||||||
|
output, err := exec.CommandContext(ctx, adb, args...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return parseEnabledNavOverlay(string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseEnabledNavOverlay reads `cmd overlay list` output and returns the
|
||||||
|
// enabled ("[x]") navigation-mode overlay package.
|
||||||
|
func parseEnabledNavOverlay(overlayList string) string {
|
||||||
|
for line := range strings.SplitSeq(overlayList, "\n") {
|
||||||
|
trimmed := strings.TrimSpace(line)
|
||||||
|
if !strings.HasPrefix(trimmed, "[x]") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
package_ := strings.TrimSpace(strings.TrimPrefix(trimmed, "[x]"))
|
||||||
|
if slices.Contains(navModeOverlays, package_) {
|
||||||
|
return package_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func navOverlayCommand(ctx context.Context, adb, serial, overlay string) *exec.Cmd {
|
||||||
|
args := []string{}
|
||||||
|
if serial != "" {
|
||||||
|
args = append(args, "-s", serial)
|
||||||
|
}
|
||||||
|
args = append(args, "shell", "cmd", "overlay", "enable-exclusive", overlay)
|
||||||
|
return exec.CommandContext(ctx, adb, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// AdbReverse sets up adb reverse forwarding for a local abstract socket.
|
// AdbReverse sets up adb reverse forwarding for a local abstract socket.
|
||||||
func AdbReverse(socket string, port int) error {
|
func AdbReverse(socket string, port int) error {
|
||||||
adb, err := AdbBinary()
|
adb, err := AdbBinary()
|
||||||
|
|||||||
@@ -201,6 +201,37 @@ func TestParseFocusedWindowPackage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseEnabledNavOverlay(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
listing string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "gesture nav active",
|
||||||
|
listing: "[ ] com.android.internal.systemui.navbar.threebutton\n[x] com.android.internal.systemui.navbar.gestural\n[ ] com.android.internal.systemui.navbar.transparent",
|
||||||
|
want: "com.android.internal.systemui.navbar.gestural",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "three-button active",
|
||||||
|
listing: "[x] com.android.internal.systemui.navbar.threebutton\n[ ] com.android.internal.systemui.navbar.gestural",
|
||||||
|
want: "com.android.internal.systemui.navbar.threebutton",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ignores enabled non-nav overlays",
|
||||||
|
listing: "[x] com.some.other.overlay\n[ ] com.android.internal.systemui.navbar.gestural",
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if got := parseEnabledNavOverlay(tc.listing); got != tc.want {
|
||||||
|
t.Errorf("parseEnabledNavOverlay = %q, want %q", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAntiFreezeCommands_DisablesFreezersAndExemptsDriver(t *testing.T) {
|
func TestAntiFreezeCommands_DisablesFreezersAndExemptsDriver(t *testing.T) {
|
||||||
commands := antiFreezeCommands()
|
commands := antiFreezeCommands()
|
||||||
joined := make([]string, len(commands))
|
joined := make([]string, len(commands))
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ func Execute(ctx context.Context, options Options, stdout io.Writer) error {
|
|||||||
if err := android.PrepareDevice(ctx, options.Device, stdout); err != nil {
|
if err := android.PrepareDevice(ctx, options.Device, stdout); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Switch to 3-button navigation for the run so fuzzer swipes cannot
|
||||||
|
// trigger the gesture-nav home/back and fling the app off screen;
|
||||||
|
// restore the original mode when the run ends.
|
||||||
|
restoreNav := android.ForceThreeButtonNav(ctx, options.Device, stdout)
|
||||||
|
defer restoreNav()
|
||||||
case "ios":
|
case "ios":
|
||||||
resolved, err := resolveIOSTarget(ctx, options, stdout)
|
resolved, err := resolveIOSTarget(ctx, options, stdout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in new issue
Block a user