Prevent privacy overlay statusbar reveal
This commit is contained in:
+152
@@ -141,6 +141,10 @@ final class StockLayoutCanvasController {
|
||||
private final Map<View, Integer> aodBeforeDrawSignatureByRoot = new WeakHashMap<>();
|
||||
private final Set<View> trackedUnlockedStockSurfaces =
|
||||
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 =
|
||||
Collections.newSetFromMap(new WeakHashMap<>());
|
||||
private final Set<View> lockedStatusBarIconSurfaceReadyRoots =
|
||||
@@ -195,6 +199,7 @@ final class StockLayoutCanvasController {
|
||||
private boolean notificationDrawerClosePending;
|
||||
private int aodVisualAlphaGeneration;
|
||||
private int systemBarRequestedVisibleTypes = WindowInsets.Type.statusBars();
|
||||
private boolean statusBarTransientShown;
|
||||
|
||||
StockLayoutCanvasController(RuntimeContext runtimeContext) {
|
||||
this.runtimeContext = runtimeContext;
|
||||
@@ -224,6 +229,7 @@ final class StockLayoutCanvasController {
|
||||
installShadeExpansionHooks(classLoader);
|
||||
installShadeHeaderAnimationGuardHook(classLoader);
|
||||
installSystemBarAttributeListener(classLoader);
|
||||
installTransientBarListener(classLoader);
|
||||
installViewRootHook(classLoader);
|
||||
hooksInstalled = true;
|
||||
}
|
||||
@@ -259,6 +265,85 @@ final class StockLayoutCanvasController {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
@@ -449,6 +534,10 @@ final class StockLayoutCanvasController {
|
||||
Object target = chain.getThisObject();
|
||||
Object alphaArg = firstArg(chain.getArgs());
|
||||
if (target instanceof View view && alphaArg instanceof Float alpha) {
|
||||
rememberPrivacyOverlayHiddenStatusBarRoot(view);
|
||||
if (shouldSuppressHiddenStatusBarRootAlpha(view, alpha)) {
|
||||
return null;
|
||||
}
|
||||
if (shouldSuppressPrivacyOverlayStatusBarAlpha(view, alpha)) {
|
||||
return null;
|
||||
}
|
||||
@@ -510,6 +599,7 @@ final class StockLayoutCanvasController {
|
||||
Object target = chain.getThisObject();
|
||||
Object translationArg = firstArg(chain.getArgs());
|
||||
if (target instanceof View view && translationArg instanceof Float translationX) {
|
||||
rememberPrivacyOverlayHiddenStatusBarRoot(view);
|
||||
if (shouldSuppressPrivacyOverlayStatusBarTranslationX(view, translationX)) {
|
||||
return null;
|
||||
}
|
||||
@@ -2219,6 +2309,10 @@ final class StockLayoutCanvasController {
|
||||
if (root == null || !hasDirectUnlockedLayoutHost(root)) {
|
||||
return;
|
||||
}
|
||||
if (!statusBarsRequestedVisible() && !statusBarTransientShown) {
|
||||
ownedIconHostManager.setUnlockedHostsTransform(root, 0f, 0f, 0f);
|
||||
return;
|
||||
}
|
||||
if (activeScene == null || !activeScene.isUnlocked()) {
|
||||
ownedIconHostManager.setUnlockedHostsTransform(root, 1f, 0f, 0f);
|
||||
return;
|
||||
@@ -3564,6 +3658,64 @@ final class StockLayoutCanvasController {
|
||||
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) {
|
||||
if (alpha >= 0.99f
|
||||
|| ownHiddenViewAlphaWriteDepth > 0
|
||||
|
||||
Reference in New Issue
Block a user