Skip to content

fix(YouTube): Always use single threaded layout to resolve layout bugs in unpatched YouTube #5226

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,36 @@ private static void findAsciiStrings(StringBuilder builder, byte[] buffer) {
}
}

/**
* Litho layout fixed thread pool size override.
* <p>
* Unpatched YouTube uses a layout fixed thread pool between 1 and 3 threads:
* <pre>
* 1 thread - > Device has less than 6 cores
* 2 threads -> Device has over 6 cores and less than 6GB of memory
* 3 threads -> Device has over 6 cores and more than 6GB of memory
* </pre>
*
* Using more than 1 thread causes layout issues such as the You tab watch/playlist shelf
* that is sometimes incorrectly hidden (ReVanced is not hiding it), and seems to
* fix a race issue if using the active navigation tab status with litho filtering.
*/
private static final int LITHO_LAYOUT_THREAD_POOL_SIZE = 1;

private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

/**
* Placeholder for actual filters.
*/
private static final class DummyFilter extends Filter { }

private static final Filter[] filters = new Filter[] {
new DummyFilter() // Replaced by patch.
};

private static final StringTrieSearch pathSearchTree = new StringTrieSearch();
private static final StringTrieSearch identifierSearchTree = new StringTrieSearch();

private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];

/**
* Because litho filtering is multi-threaded and the buffer is passed in from a different injection point,
* the buffer is saved to a ThreadLocal so each calling thread does not interfere with other threads.
Expand Down Expand Up @@ -213,9 +234,28 @@ private static boolean handleFiltering(@Nullable String lithoIdentifier, StringB

return false;
}
}

/**
* Placeholder for actual filters.
*/
final class DummyFilter extends Filter { }
/**
* Injection point.
*/
public static int getExecutorCorePoolSize(int originalCorePoolSize) {
if (originalCorePoolSize != LITHO_LAYOUT_THREAD_POOL_SIZE) {
Logger.printDebug(() -> "Overriding core thread pool size from: " + originalCorePoolSize
+ " to: " + LITHO_LAYOUT_THREAD_POOL_SIZE);
}

return LITHO_LAYOUT_THREAD_POOL_SIZE;
}

/**
* Injection point.
*/
public static int getExecutorMaxThreads(int originalMaxThreads) {
if (originalMaxThreads != LITHO_LAYOUT_THREAD_POOL_SIZE) {
Logger.printDebug(() -> "Overriding max thread pool size from: " + originalMaxThreads
+ " to: " + LITHO_LAYOUT_THREAD_POOL_SIZE);
}

return LITHO_LAYOUT_THREAD_POOL_SIZE;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.misc.litho.filter

import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
Expand Down Expand Up @@ -52,6 +53,15 @@ internal val emptyComponentFingerprint = fingerprint {
}
}

internal val lithoThreadExecutorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters("I", "I", "I")
custom { method, classDef ->
classDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" &&
method.containsLiteralInstruction(1L) // 1L = default thread timeout.
}
}

internal val lithoComponentNameUpbFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,22 @@ val lithoFilterPatch = bytecodePatch(

// endregion


// region Change Litho thread executor to 1 thread to fix layout issue in unpatched YouTube.

lithoThreadExecutorFingerprint.method.addInstructions(
0,
"""
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorCorePoolSize(I)I
move-result p1
invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorMaxThreads(I)I
move-result p2
"""
)

// endregion


// region A/B test of new Litho native code.

// Turn off native code that handles litho component names. If this feature is on then nearly
Expand Down