Reland "[WebLayer] Change the return type of Fragment.getActivity() to Activity."

This is a reland of ae52b2a4a4f3fdf121619fc62f300883b610bd46 with fixes
for robolectric tests, which run on the JVM which appears to have
stricter verification checks.

Original change's description:
> [WebLayer] Change the return type of Fragment.getActivity() to Activity.
>
> This CL adds the ability to specify a script in an android_aar_prebuilt
> rule that can modify prebuilt jar files, and adds a script to change
> Fragment.getActivity() and Fragment.requireActivity() to return an
> Activity instead of a FragmentActivity.
>
> This is the first CL in a chain that will allow us to remove the fake
> activity we create when embedding Fragments that cross classloader
> boundaries.
>
> Bug: 1123216
> Change-Id: I4b9d3ca5f9c3a4d86e08d64f49d601c08fca9a70
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2432413
> Reviewed-by: Theresa  <[email protected]>
> Reviewed-by: Andrew Grieve <[email protected]>
> Commit-Queue: Robbie McElrath <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#823582}

Bug: 1123216
Change-Id: I83ed0c47cda12e4a71ed90cc0bce4cde2a07782d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2521343
Reviewed-by: Andrew Grieve <[email protected]>
Reviewed-by: Theresa  <[email protected]>
Commit-Queue: Robbie McElrath <[email protected]>
Cr-Commit-Position: refs/heads/master@{#826944}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index dec3bd3..0b09333e 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1306,6 +1306,7 @@
     'build/android/gyp/apkbuilder.pydeps',
     'build/android/gyp/assert_static_initializers.pydeps',
     'build/android/gyp/bytecode_processor.pydeps',
+    'build/android/gyp/bytecode_rewriter.pydeps',
     'build/android/gyp/compile_java.pydeps',
     'build/android/gyp/compile_resources.pydeps',
     'build/android/gyp/copy_ex.pydeps',
diff --git a/build/android/bytecode/BUILD.gn b/build/android/bytecode/BUILD.gn
index 4d29aca..b56f341 100644
--- a/build/android/bytecode/BUILD.gn
+++ b/build/android/bytecode/BUILD.gn
@@ -18,3 +18,17 @@
   wrapper_script_name = "helper/bytecode_processor"
   enable_bytecode_checks = false
 }
+
+java_binary("fragment_activity_replacer") {
+  sources = [
+    "java/org/chromium/bytecode/ByteCodeRewriter.java",
+    "java/org/chromium/bytecode/FragmentActivityReplacer.java",
+  ]
+  main_class = "org.chromium.bytecode.FragmentActivityReplacer"
+  deps = [
+    "//third_party/android_deps:org_ow2_asm_asm_commons_java",
+    "//third_party/android_deps:org_ow2_asm_asm_java",
+    "//third_party/android_deps:org_ow2_asm_asm_util_java",
+  ]
+  wrapper_script_name = "helper/fragment_activity_replacer"
+}
diff --git a/build/android/bytecode/java/org/chromium/bytecode/ByteCodeRewriter.java b/build/android/bytecode/java/org/chromium/bytecode/ByteCodeRewriter.java
new file mode 100644
index 0000000..3d0d9cdd
--- /dev/null
+++ b/build/android/bytecode/java/org/chromium/bytecode/ByteCodeRewriter.java
@@ -0,0 +1,91 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.bytecode;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Base class for scripts that perform bytecode modifications on a jar file.
+ */
+public abstract class ByteCodeRewriter {
+    private static final String CLASS_FILE_SUFFIX = ".class";
+
+    public void rewrite(File inputJar, File outputJar) throws IOException {
+        if (!inputJar.exists()) {
+            throw new FileNotFoundException("Input jar not found: " + inputJar.getPath());
+        }
+        try (InputStream inputStream = new BufferedInputStream(new FileInputStream(inputJar))) {
+            try (OutputStream outputStream = new FileOutputStream(outputJar)) {
+                processZip(inputStream, outputStream);
+            }
+        }
+    }
+
+    /** Returns true if the class at the given path in the archive should be rewritten. */
+    protected abstract boolean shouldRewriteClass(String classPath);
+
+    /**
+     * Returns the ClassVisitor that should be used to modify the bytecode of class at the given
+     * path in the archive.
+     */
+    protected abstract ClassVisitor getClassVisitorForClass(
+            String classPath, ClassVisitor delegate);
+
+    private void processZip(InputStream inputStream, OutputStream outputStream) {
+        try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
+            ZipInputStream zipInputStream = new ZipInputStream(inputStream);
+            ZipEntry entry;
+            while ((entry = zipInputStream.getNextEntry()) != null) {
+                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+                boolean handled = processClassEntry(entry, zipInputStream, buffer);
+                if (handled) {
+                    ZipEntry newEntry = new ZipEntry(entry.getName());
+                    zipOutputStream.putNextEntry(newEntry);
+                    zipOutputStream.write(buffer.toByteArray(), 0, buffer.size());
+                } else {
+                    zipOutputStream.putNextEntry(entry);
+                    zipInputStream.transferTo(zipOutputStream);
+                }
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private boolean processClassEntry(
+            ZipEntry entry, InputStream inputStream, OutputStream outputStream) {
+        if (!entry.getName().endsWith(CLASS_FILE_SUFFIX) || !shouldRewriteClass(entry.getName())) {
+            return false;
+        }
+        try {
+            ClassReader reader = new ClassReader(inputStream);
+            ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
+            ClassVisitor classVisitor = getClassVisitorForClass(entry.getName(), writer);
+            reader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
+
+            writer.visitEnd();
+            byte[] classData = writer.toByteArray();
+            outputStream.write(classData, 0, classData.length);
+            return true;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/build/android/bytecode/java/org/chromium/bytecode/FragmentActivityReplacer.java b/build/android/bytecode/java/org/chromium/bytecode/FragmentActivityReplacer.java
new file mode 100644
index 0000000..2199588b1
--- /dev/null
+++ b/build/android/bytecode/java/org/chromium/bytecode/FragmentActivityReplacer.java
@@ -0,0 +1,119 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.bytecode;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.MethodRemapper;
+import org.objectweb.asm.commons.Remapper;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Java application that modifies Fragment.getActivity() to return an Activity instead of a
+ * FragmentActivity, and updates any existing getActivity() calls to reference the updated method.
+ *
+ * See crbug.com/1144345 for more context.
+ */
+public class FragmentActivityReplacer extends ByteCodeRewriter {
+    private static final String FRAGMENT_CLASS_PATH = "androidx/fragment/app/Fragment.class";
+    private static final String FRAGMENT_ACTIVITY_INTERNAL_CLASS_NAME =
+            "androidx/fragment/app/FragmentActivity";
+    private static final String ACTIVITY_INTERNAL_CLASS_NAME = "android/app/Activity";
+    private static final String GET_ACTIVITY_METHOD_NAME = "getActivity";
+    private static final String REQUIRE_ACTIVITY_METHOD_NAME = "requireActivity";
+    private static final String OLD_METHOD_DESCRIPTOR =
+            "()Landroidx/fragment/app/FragmentActivity;";
+    private static final String NEW_METHOD_DESCRIPTOR = "()Landroid/app/Activity;";
+
+    public static void main(String[] args) throws IOException {
+        // Invoke this script using //build/android/gyp/bytecode_processor.py
+        if (args.length != 2) {
+            System.err.println("Expected 2 arguments: [input.jar] [output.jar]");
+            System.exit(1);
+        }
+
+        FragmentActivityReplacer rewriter = new FragmentActivityReplacer();
+        rewriter.rewrite(new File(args[0]), new File(args[1]));
+    }
+
+    @Override
+    protected boolean shouldRewriteClass(String classPath) {
+        return true;
+    }
+
+    @Override
+    protected ClassVisitor getClassVisitorForClass(String classPath, ClassVisitor delegate) {
+        ClassVisitor getActivityReplacer = new GetActivityReplacer(delegate);
+        if (classPath.equals(FRAGMENT_CLASS_PATH)) {
+            return new FragmentClassVisitor(getActivityReplacer);
+        }
+        return getActivityReplacer;
+    }
+
+    /** Updates any Fragment.getActivity/requireActivity() calls to call the replaced method. */
+    private static class GetActivityReplacer extends ClassVisitor {
+        private GetActivityReplacer(ClassVisitor baseVisitor) {
+            super(Opcodes.ASM7, baseVisitor);
+        }
+
+        @Override
+        public MethodVisitor visitMethod(
+                int access, String name, String descriptor, String signature, String[] exceptions) {
+            MethodVisitor base = super.visitMethod(access, name, descriptor, signature, exceptions);
+            return new MethodVisitor(Opcodes.ASM7, base) {
+                @Override
+                public void visitMethodInsn(int opcode, String owner, String name,
+                        String descriptor, boolean isInterface) {
+                    if ((opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKESPECIAL)
+                            && descriptor.equals(OLD_METHOD_DESCRIPTOR)
+                            && (name.equals(GET_ACTIVITY_METHOD_NAME)
+                                    || name.equals(REQUIRE_ACTIVITY_METHOD_NAME))) {
+                        super.visitMethodInsn(
+                                opcode, owner, name, NEW_METHOD_DESCRIPTOR, isInterface);
+                    } else {
+                        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+                    }
+                }
+            };
+        }
+    }
+
+    /**
+     * Makes Fragment.getActivity() and Fragment.requireActivity() non-final, and changes their
+     * return types to Activity.
+     */
+    private static class FragmentClassVisitor extends ClassVisitor {
+        private FragmentClassVisitor(ClassVisitor baseVisitor) {
+            super(Opcodes.ASM7, baseVisitor);
+        }
+
+        @Override
+        public MethodVisitor visitMethod(
+                int access, String name, String descriptor, String signature, String[] exceptions) {
+            MethodVisitor base;
+            // Update the descriptor of getActivity/requireActivity, and make them non-final.
+            if (name.equals(GET_ACTIVITY_METHOD_NAME)
+                    || name.equals(REQUIRE_ACTIVITY_METHOD_NAME)) {
+                base = super.visitMethod(
+                        access & ~Opcodes.ACC_FINAL, name, NEW_METHOD_DESCRIPTOR, null, exceptions);
+            } else {
+                base = super.visitMethod(access, name, descriptor, signature, exceptions);
+            }
+
+            return new MethodRemapper(base, new Remapper() {
+                @Override
+                public String mapType(String internalName) {
+                    if (internalName.equals(FRAGMENT_ACTIVITY_INTERNAL_CLASS_NAME)) {
+                        return ACTIVITY_INTERNAL_CLASS_NAME;
+                    }
+                    return internalName;
+                }
+            });
+        }
+    }
+}
diff --git a/build/android/gyp/bytecode_rewriter.py b/build/android/gyp/bytecode_rewriter.py
new file mode 100755
index 0000000..b37ef6f
--- /dev/null
+++ b/build/android/gyp/bytecode_rewriter.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Wrapper script around ByteCodeRewriter subclass scripts."""
+
+import argparse
+import sys
+
+from util import build_utils
+
+
+def main(argv):
+  argv = build_utils.ExpandFileArgs(argv[1:])
+  parser = argparse.ArgumentParser()
+  build_utils.AddDepfileOption(parser)
+  parser.add_argument('--script',
+                      required=True,
+                      help='Path to the java binary wrapper script.')
+  parser.add_argument('--classpath', action='append', nargs='+')
+  parser.add_argument('--input-jar', required=True)
+  parser.add_argument('--output-jar', required=True)
+  args = parser.parse_args(argv)
+
+  classpath = build_utils.ParseGnList(args.classpath)
+  build_utils.WriteDepfile(args.depfile, args.output_jar, inputs=classpath)
+
+  classpath.append(args.input_jar)
+  cmd = [
+      args.script, '--classpath', ':'.join(classpath), args.input_jar,
+      args.output_jar
+  ]
+  build_utils.CheckOutput(cmd, print_stdout=True)
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/build/android/gyp/bytecode_rewriter.pydeps b/build/android/gyp/bytecode_rewriter.pydeps
new file mode 100644
index 0000000..b8f304a
--- /dev/null
+++ b/build/android/gyp/bytecode_rewriter.pydeps
@@ -0,0 +1,6 @@
+# Generated by running:
+#   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/bytecode_rewriter.pydeps build/android/gyp/bytecode_rewriter.py
+../../gn_helpers.py
+bytecode_rewriter.py
+util/__init__.py
+util/build_utils.py
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index e4468d8..f79f26ef 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -3982,8 +3982,56 @@
       }
     }  # _has_sources
 
-    # TODO(crbug.com/1123216): Implement bytecode_rewriter_target.
-    not_needed(invoker, [ "bytecode_rewriter_target" ])
+    if (_is_prebuilt || _build_device_jar || _build_host_jar) {
+      _unprocessed_jar_deps = _full_classpath_deps
+      if (_has_sources) {
+        _unprocessed_jar_deps += [ ":$_compile_java_target" ]
+      }
+    }
+
+    if (defined(invoker.bytecode_rewriter_target)) {
+      assert(_build_host_jar || _build_device_jar,
+             "A host or device jar must be created to use bytecode rewriting")
+
+      _rewritten_jar =
+          string_replace(_unprocessed_jar_path, ".jar", "_rewritten.jar")
+      _rewritten_jar_target_name = "${target_name}__rewritten"
+      _rewriter_path = root_build_dir + "/bin/helper/" +
+                       get_label_info(invoker.bytecode_rewriter_target, "name")
+      _rebased_build_config = rebase_path(_build_config, root_build_dir)
+      action_with_pydeps(_rewritten_jar_target_name) {
+        script = "//build/android/gyp/bytecode_rewriter.py"
+        inputs = [
+          _rewriter_path,
+          _build_config,
+          _unprocessed_jar_path,
+        ]
+        outputs = [ _rewritten_jar ]
+        depfile = "$target_gen_dir/$target_name.d"
+        args = [
+          "--depfile",
+          rebase_path(depfile, root_build_dir),
+          "--script",
+          rebase_path(_rewriter_path, root_build_dir),
+          "--classpath",
+          "@FileArg($_rebased_build_config:deps_info:javac_full_classpath)",
+          "--classpath",
+          "@FileArg($_rebased_build_config:android:sdk_jars)",
+          "--input-jar",
+          rebase_path(_unprocessed_jar_path, root_build_dir),
+          "--output-jar",
+          rebase_path(_rewritten_jar, root_build_dir),
+        ]
+        deps = _unprocessed_jar_deps + [
+                 ":$_build_config_target_name",
+                 invoker.bytecode_rewriter_target,
+               ]
+      }
+
+      _unprocessed_jar_deps = []
+      _unprocessed_jar_deps = [ ":$_rewritten_jar_target_name" ]
+      _unprocessed_jar_path = _rewritten_jar
+    }
 
     if (_is_prebuilt) {
       generate_interface_jar(_header_target_name) {
@@ -3998,10 +4046,7 @@
         # target. If we can change compile & desugar steps to use direct
         # interface classpath rather than full interface classpath, then this
         # could just be _non_java_deps.
-        deps = _classpath_deps
-        if (_has_sources) {
-          deps += [ ":$_compile_java_target" ]
-        }
+        deps = _unprocessed_jar_deps
       }
       _public_deps += [ ":$_header_target_name" ]
     }
@@ -4017,10 +4062,7 @@
         build_config = _build_config
         build_config_dep = ":$_build_config_target_name"
         input_jar_path = _unprocessed_jar_path
-        jar_deps = _non_java_deps
-        if (_has_sources) {
-          jar_deps += [ ":$_compile_java_target" ]
-        }
+        jar_deps = _unprocessed_jar_deps
         if (_build_host_jar) {
           host_jar_path = _host_processed_jar_path
         }
@@ -4063,10 +4105,7 @@
         _bytecode_checks_target = "${target_name}__validate_classpath"
         bytecode_processor(_bytecode_checks_target) {
           forward_variables_from(invoker, [ "missing_classes_allowlist" ])
-          deps = _full_classpath_deps
-          if (_has_sources) {
-            deps += [ ":$_compile_java_target" ]
-          }
+          deps = _unprocessed_jar_deps + [ ":$_build_config_target_name" ]
           requires_android = _requires_android
           target_label =
               get_label_info(":${invoker.target_name}", "label_no_toolchain")
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFragment.java
index baeda336..3d57b546 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFragment.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.firstrun;
 
-import androidx.fragment.app.FragmentActivity;
+import android.app.Activity;
 
 /**
  * This interface is implemented by FRE fragments.
@@ -25,7 +25,7 @@
     /**
      * @see Fragment#getActivity().
      */
-    FragmentActivity getActivity();
+    Activity getActivity();
 
     /**
      * Set the a11y focus when the fragment is shown on the screen.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordEntryEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordEntryEditor.java
index bbb98097..d2782bf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordEntryEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordEntryEditor.java
@@ -141,8 +141,7 @@
         }
         mPendingAction = action;
         ReauthenticationManager.displayReauthenticationFragment(reasonString, View.NO_ID,
-                getActivity().getSupportFragmentManager(),
-                ReauthenticationManager.ReauthScope.ONE_AT_A_TIME);
+                getParentFragmentManager(), ReauthenticationManager.ReauthScope.ONE_AT_A_TIME);
     }
 
     @Override
@@ -150,4 +149,4 @@
         super.onDestroy();
         PasswordEditingDelegateProvider.getInstance().getPasswordEditingDelegate().destroy();
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/webauthn/android/java/src/org/chromium/chrome/browser/webauthn/CableAuthenticatorModuleProvider.java b/chrome/browser/webauthn/android/java/src/org/chromium/chrome/browser/webauthn/CableAuthenticatorModuleProvider.java
index 3e3796e..9c8f685 100644
--- a/chrome/browser/webauthn/android/java/src/org/chromium/chrome/browser/webauthn/CableAuthenticatorModuleProvider.java
+++ b/chrome/browser/webauthn/android/java/src/org/chromium/chrome/browser/webauthn/CableAuthenticatorModuleProvider.java
@@ -84,8 +84,7 @@
     private void showModule() {
         mStatus.setText("Installed.");
 
-        FragmentTransaction transaction =
-                getActivity().getSupportFragmentManager().beginTransaction();
+        FragmentTransaction transaction = getParentFragmentManager().beginTransaction();
         Fragment fragment = Cablev2AuthenticatorModule.getImpl().getFragment();
         Bundle arguments = getArguments();
         if (arguments == null) {
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 01850286..eca9299 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -366,6 +366,9 @@
   jar_excluded_patterns = [ "androidx/fragment/app/DialogFragment*" ]
 
   ignore_proguard_configs = true
+
+  bytecode_rewriter_target =
+      "//build/android/bytecode:fragment_activity_replacer"
 }
 
 # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
@@ -577,6 +580,8 @@
     "androidx/preference/PreferenceFragmentCompat*",
   ]
 
+  bytecode_rewriter_target =
+      "//build/android/bytecode:fragment_activity_replacer"
   ignore_proguard_configs = true
 }
 
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
index f83c1f95..e88767d 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -354,6 +354,8 @@
                 |  ]
                 |
                 |  ignore_proguard_configs = true
+                |
+                |  bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer"
                 |""".stripMargin())
                 break
             case 'androidx_media_media':
@@ -496,6 +498,7 @@
                 |    "androidx/preference/PreferenceFragmentCompat*",
                 |  ]
                 |
+                |  bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer"
                 |""".stripMargin())
                 // Replace broad library -keep rules with a more limited set in
                 // chrome/android/java/proguard.flags instead.
diff --git a/third_party/android_deps/local_modifications/androidx_preference_preference/README b/third_party/android_deps/local_modifications/androidx_preference_preference/README
index f4fb51c..697cd14 100644
--- a/third_party/android_deps/local_modifications/androidx_preference_preference/README
+++ b/third_party/android_deps/local_modifications/androidx_preference_preference/README
@@ -1,8 +1,8 @@
 This directory contains PreferenceFragmentCompat.java and
 PreferenceDialogFragmentCompat.java, copied without changes from the AndroidX
-preference library at commit beeb6fb. These files contain two changes (commits
-72c0381 and beeb6fb) that we want in Chromium, but are not yet in an official
-preference library release.
+preference library at commit e865a9b. These files contain three changes
+(commits 72c0381, beeb6fb, and e865a9b) that we want in Chromium, but are not
+yet in an official preference library release.
 
 To pull in these changes, we exclude PreferenceFragmentCompat and
 PreferenceDialogFragmentCompat from the androidx_preference_preference library
diff --git a/third_party/android_deps/local_modifications/androidx_preference_preference/androidx_preference_preference_java.jar b/third_party/android_deps/local_modifications/androidx_preference_preference/androidx_preference_preference_java.jar
index 201cd50..de88d15 100644
--- a/third_party/android_deps/local_modifications/androidx_preference_preference/androidx_preference_preference_java.jar
+++ b/third_party/android_deps/local_modifications/androidx_preference_preference/androidx_preference_preference_java.jar
Binary files differ
diff --git a/third_party/android_deps/local_modifications/androidx_preference_preference/java/androidx/preference/PreferenceFragmentCompat.java b/third_party/android_deps/local_modifications/androidx_preference_preference/java/androidx/preference/PreferenceFragmentCompat.java
index 1fa26698..5922dfa 100644
--- a/third_party/android_deps/local_modifications/androidx_preference_preference/java/androidx/preference/PreferenceFragmentCompat.java
+++ b/third_party/android_deps/local_modifications/androidx_preference_preference/java/androidx/preference/PreferenceFragmentCompat.java
@@ -61,7 +61,7 @@
  *
  * <p>To build a hierarchy from code, use
  * {@link PreferenceManager#createPreferenceScreen(Context)} to create the root
- * {@link PreferenceScreen}. Once you have added other {@link Preference}s to this root scree
+ * {@link PreferenceScreen}. Once you have added other {@link Preference}s to this root screen
  * with {@link PreferenceScreen#addPreference(Preference)}, you then need to set the screen as
  * the root screen in your hierarchy with {@link #setPreferenceScreen(PreferenceScreen)}.
  *
@@ -420,8 +420,7 @@
                                 + "implement this method so that you can configure the new "
                                 + "fragment that will be displayed, and set a transition between "
                                 + "the fragments.");
-                final FragmentManager fragmentManager = requireActivity()
-                        .getSupportFragmentManager();
+                final FragmentManager fragmentManager = getParentFragmentManager();
                 final Bundle args = preference.getExtras();
                 final Fragment fragment = fragmentManager.getFragmentFactory().instantiate(
                         requireActivity().getClassLoader(), preference.getFragment());
@@ -457,7 +456,7 @@
                     .onPreferenceStartScreen(this, preferenceScreen);
         }
         if (!handled && getContext() instanceof OnPreferenceStartScreenCallback) {
-            ((OnPreferenceStartScreenCallback) getContext())
+            handled = ((OnPreferenceStartScreenCallback) getContext())
                     .onPreferenceStartScreen(this, preferenceScreen);
         }
         // Check the Activity as well in case getContext was overridden to return something other