Prevent privacy overlay statusbar reveal

This commit is contained in:
ajp_anton
2026-07-09 02:54:12 +00:00
parent 3ebebb5218
commit 624b5ee6b7
@@ -141,6 +141,10 @@ final class StockLayoutCanvasController {
private final Map<View, Integer> aodBeforeDrawSignatureByRoot = new WeakHashMap<>(); private final Map<View, Integer> aodBeforeDrawSignatureByRoot = new WeakHashMap<>();
private final Set<View> trackedUnlockedStockSurfaces = private final Set<View> trackedUnlockedStockSurfaces =
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> suppressedHiddenStatusBarRoots =
Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> privacyOverlayHiddenStatusBarRoots =
Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> trackedLockedStatusBarIconSurfaces = private final Set<View> trackedLockedStatusBarIconSurfaces =
Collections.newSetFromMap(new WeakHashMap<>()); Collections.newSetFromMap(new WeakHashMap<>());
private final Set<View> lockedStatusBarIconSurfaceReadyRoots = private final Set<View> lockedStatusBarIconSurfaceReadyRoots =
@@ -195,6 +199,7 @@ final class StockLayoutCanvasController {
private boolean notificationDrawerClosePending; private boolean notificationDrawerClosePending;
private int aodVisualAlphaGeneration; private int aodVisualAlphaGeneration;
private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars(); private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars();
private boolean statusBarTransientShown;
StockLayoutCanvasController(RuntimeContext runtimeContext) { StockLayoutCanvasController(RuntimeContext runtimeContext) {
this.runtimeContext = runtimeContext; this.runtimeContext = runtimeContext;
@@ -224,6 +229,7 @@ final class StockLayoutCanvasController {
installShadeExpansionHooks(classLoader); installShadeExpansionHooks(classLoader);
installShadeHeaderAnimationGuardHook(classLoader); installShadeHeaderAnimationGuardHook(classLoader);
installSystemBarAttributeListener(classLoader); installSystemBarAttributeListener(classLoader);
installTransientBarListener(classLoader);
installViewRootHook(classLoader); installViewRootHook(classLoader);
hooksInstalled = true; hooksInstalled = true;
} }
@@ -259,6 +265,85 @@ final class StockLayoutCanvasController {
return; return;
} }
systemBarRequestedVisibleTypes = requestedVisibleTypes; systemBarRequestedVisibleTypes = requestedVisibleTypes;
if (statusBarsRequestedVisible()) {
releaseSuppressedHiddenStatusBarRoots();
privacyOverlayHiddenStatusBarRoots.clear();
}
}
private void installTransientBarListener(ClassLoader classLoader) {
Class<?> commandQueueClass = ReflectionSupport.findClassIfExists(
"com.android.systemui.statusbar.CommandQueue",
classLoader);
if (commandQueueClass != null) {
XposedHookSupport.hookAllMethodsIfExists(
runtimeContext.getFramework(),
commandQueueClass,
"showTransient",
chain -> {
Object result = chain.proceed();
updateTransientBarState(chain.getArgs(), true);
return result;
});
XposedHookSupport.hookAllMethodsIfExists(
runtimeContext.getFramework(),
commandQueueClass,
"abortTransient",
chain -> {
Object result = chain.proceed();
updateTransientBarState(chain.getArgs(), false);
return result;
});
}
installStatusBarAutoHideElementListener(classLoader);
}
private void installStatusBarAutoHideElementListener(ClassLoader classLoader) {
Class<?> statusBarAutoHideElementClass = ReflectionSupport.findClassIfExists(
"com.android.systemui.statusbar.phone.CentralSurfacesImpl$5",
classLoader);
if (statusBarAutoHideElementClass == null) {
return;
}
XposedHookSupport.hookAllMethodsIfExists(
runtimeContext.getFramework(),
statusBarAutoHideElementClass,
"hide",
chain -> {
Object result = chain.proceed();
updateStatusBarTransientShown(false);
return result;
});
}
private void updateTransientBarState(List<Object> args, boolean visible) {
Integer displayId = args != null && !args.isEmpty() ? asInteger(args.get(0)) : null;
boolean statusBarsAffected = transientArgsIncludeStatusBars(args);
if (displayId == null || displayId != 0 || !statusBarsAffected) {
return;
}
updateStatusBarTransientShown(visible);
}
private void updateStatusBarTransientShown(boolean shown) {
statusBarTransientShown = shown;
}
private boolean transientArgsIncludeStatusBars(List<Object> args) {
if (args == null || args.size() < 2) {
return true;
}
Object typesArg = args.get(1);
if (!(typesArg instanceof int[] types)) {
return true;
}
int statusBars = WindowInsets.Type.statusBars();
for (int type : types) {
if ((type & statusBars) != 0) {
return true;
}
}
return false;
} }
private void installNotificationCollectionHooks(ClassLoader classLoader) { private void installNotificationCollectionHooks(ClassLoader classLoader) {
@@ -449,6 +534,10 @@ final class StockLayoutCanvasController {
Object target = chain.getThisObject(); Object target = chain.getThisObject();
Object alphaArg = firstArg(chain.getArgs()); Object alphaArg = firstArg(chain.getArgs());
if (target instanceof View view && alphaArg instanceof Float alpha) { if (target instanceof View view && alphaArg instanceof Float alpha) {
rememberPrivacyOverlayHiddenStatusBarRoot(view);
if (shouldSuppressHiddenStatusBarRootAlpha(view, alpha)) {
return null;
}
if (shouldSuppressPrivacyOverlayStatusBarAlpha(view, alpha)) { if (shouldSuppressPrivacyOverlayStatusBarAlpha(view, alpha)) {
return null; return null;
} }
@@ -510,6 +599,7 @@ final class StockLayoutCanvasController {
Object target = chain.getThisObject(); Object target = chain.getThisObject();
Object translationArg = firstArg(chain.getArgs()); Object translationArg = firstArg(chain.getArgs());
if (target instanceof View view && translationArg instanceof Float translationX) { if (target instanceof View view && translationArg instanceof Float translationX) {
rememberPrivacyOverlayHiddenStatusBarRoot(view);
if (shouldSuppressPrivacyOverlayStatusBarTranslationX(view, translationX)) { if (shouldSuppressPrivacyOverlayStatusBarTranslationX(view, translationX)) {
return null; return null;
} }
@@ -2219,6 +2309,10 @@ final class StockLayoutCanvasController {
if (root == null || !hasDirectUnlockedLayoutHost(root)) { if (root == null || !hasDirectUnlockedLayoutHost(root)) {
return; return;
} }
if (!statusBarsRequestedVisible() && !statusBarTransientShown) {
ownedIconHostManager.setUnlockedHostsTransform(root, 0f, 0f, 0f);
return;
}
if (activeScene == null || !activeScene.isUnlocked()) { if (activeScene == null || !activeScene.isUnlocked()) {
ownedIconHostManager.setUnlockedHostsTransform(root, 1f, 0f, 0f); ownedIconHostManager.setUnlockedHostsTransform(root, 1f, 0f, 0f);
return; return;
@@ -3564,6 +3658,64 @@ final class StockLayoutCanvasController {
return true; return true;
} }
private boolean shouldSuppressHiddenStatusBarRootAlpha(View view, float alpha) {
boolean privacyOverlayRoot = privacyOverlayHiddenStatusBarRoots.contains(view);
boolean suppress = alpha > 0f
&& ownHiddenViewAlphaWriteDepth <= 0
&& isPhoneStatusBarRoot(view)
&& view.isAttachedToWindow()
&& isLayoutEnabled(view.getContext())
&& !statusBarsRequestedVisible()
&& (privacyOverlayRoot || !statusBarTransientShown);
if (suppress) {
suppressedHiddenStatusBarRoots.add(view);
}
return suppress;
}
private void rememberPrivacyOverlayHiddenStatusBarRoot(View view) {
if (view == null
|| !isPhoneStatusBarRoot(view)
|| !view.isAttachedToWindow()
|| statusBarsRequestedVisible()
|| !isLayoutEnabled(view.getContext())
|| ongoingPrivacyChipInRoot(view) == null) {
return;
}
privacyOverlayHiddenStatusBarRoots.add(view);
}
private void releaseSuppressedHiddenStatusBarRoots() {
if (suppressedHiddenStatusBarRoots.isEmpty()) {
return;
}
ArrayDeque<View> staleRoots = new ArrayDeque<>();
for (View root : suppressedHiddenStatusBarRoots) {
if (root == null || !root.isAttachedToWindow()) {
staleRoots.add(root);
continue;
}
if (!isLayoutEnabled(root.getContext())) {
staleRoots.add(root);
continue;
}
Runnable restore = () -> {
if (root.isAttachedToWindow()
&& statusBarsRequestedVisible()
&& root.getAlpha() <= 0f) {
root.setAlpha(1f);
}
};
if (root.getHandler() != null) {
root.post(restore);
} else {
restore.run();
}
staleRoots.add(root);
}
removeStaleViews(suppressedHiddenStatusBarRoots, staleRoots);
}
private boolean shouldSuppressPrivacyOverlayStatusBarAlpha(View view, float alpha) { private boolean shouldSuppressPrivacyOverlayStatusBarAlpha(View view, float alpha) {
if (alpha >= 0.99f if (alpha >= 0.99f
|| ownHiddenViewAlphaWriteDepth > 0 || ownHiddenViewAlphaWriteDepth > 0