blob: 41d9dbf894f48f57f0ec32ecbe8516a566d5c3ff [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static android.os.PowerManager.THERMAL_STATUS_CRITICAL;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.provider.Settings;
import android.view.ICrossWindowBlurEnabledListener;
import android.view.TunnelModeEnabledListener;
/**
* Keeps track of the different factors that determine whether cross-window blur is enabled
* or disabled. Also keeps a list of all interested listeners and notifies them when the
* blur enabled state changes.
*/
final class BlurController {
private final Context mContext;
private final RemoteCallbackList<ICrossWindowBlurEnabledListener>
mBlurEnabledListeners = new RemoteCallbackList<>();
// We don't use the WM global lock, because the BlurController is not involved in window
// drawing and only receives binder calls that don't need synchronization with the rest of WM
private final Object mLock = new Object();
private volatile boolean mBlurEnabled;
private boolean mInPowerSaveMode;
private boolean mCriticalThermalStatus;
private boolean mBlurDisabledSetting;
private boolean mTunnelModeEnabled = false;
private TunnelModeEnabledListener mTunnelModeListener =
new TunnelModeEnabledListener(Runnable::run) {
@Override
public void onTunnelModeEnabledChanged(boolean tunnelModeEnabled) {
mTunnelModeEnabled = tunnelModeEnabled;
updateBlurEnabled();
}
};
BlurController(Context context, PowerManager powerManager) {
mContext = context;
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
context.registerReceiverForAllUsers(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) {
// onReceive always gets called on the same thread, so there is no
// multi-threaded execution here. Thus, we don't have to hold mLock here.
mInPowerSaveMode = powerManager.isPowerSaveMode();
updateBlurEnabled();
}
}
}, filter, null, null);
mInPowerSaveMode = powerManager.isPowerSaveMode();
context.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.DISABLE_WINDOW_BLURS), false,
new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// onChange always gets called on the same thread, so there is no
// multi-threaded execution here. Thus, we don't have to hold mLock here.
mBlurDisabledSetting = getBlurDisabledSetting();
updateBlurEnabled();
}
});
mBlurDisabledSetting = getBlurDisabledSetting();
powerManager.addThermalStatusListener((status) -> {
mCriticalThermalStatus = status >= THERMAL_STATUS_CRITICAL;
updateBlurEnabled();
});
mCriticalThermalStatus = powerManager.getCurrentThermalStatus() >= THERMAL_STATUS_CRITICAL;
TunnelModeEnabledListener.register(mTunnelModeListener);
updateBlurEnabled();
}
boolean registerCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener) {
if (listener == null) return false;
mBlurEnabledListeners.register(listener);
return getBlurEnabled();
}
void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener) {
if (listener == null) return;
mBlurEnabledListeners.unregister(listener);
}
boolean getBlurEnabled() {
return mBlurEnabled;
}
private void updateBlurEnabled() {
synchronized (mLock) {
final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurDisabledSetting
&& !mInPowerSaveMode && !mTunnelModeEnabled && !mCriticalThermalStatus;
if (mBlurEnabled == newEnabled) {
return;
}
mBlurEnabled = newEnabled;
notifyBlurEnabledChangedLocked(newEnabled);
}
}
private void notifyBlurEnabledChangedLocked(boolean enabled) {
int i = mBlurEnabledListeners.beginBroadcast();
while (i > 0) {
i--;
ICrossWindowBlurEnabledListener listener =
mBlurEnabledListeners.getBroadcastItem(i);
try {
listener.onCrossWindowBlurEnabledChanged(enabled);
} catch (RemoteException e) {
}
}
mBlurEnabledListeners.finishBroadcast();
}
private boolean getBlurDisabledSetting() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DISABLE_WINDOW_BLURS, 0) == 1;
}
}