Merge "Auto-generate list of module impl jars"
diff --git a/Android.bp b/Android.bp
index 204a6ce..ab85a0a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -396,7 +396,6 @@
         // TODO: remove these annotations as soon as we can use andoid.support.annotations.*
         ":framework-annotations",
         ":modules-utils-preconditions-srcs",
-        "core/java/android/net/DhcpResults.java",
         "core/java/android/util/IndentingPrintWriter.java",
         "core/java/android/util/LocalLog.java",
         "core/java/com/android/internal/util/HexDump.java",
diff --git a/core/api/current.txt b/core/api/current.txt
index 6a72017..d6a7915 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29675,6 +29675,7 @@
     field public static final int Q = 29; // 0x1d
     field public static final int R = 30; // 0x1e
     field public static final int S = 31; // 0x1f
+    field public static final int S_V2 = 32; // 0x20
     field public static final int TIRAMISU = 10000; // 0x2710
   }
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 3c483f4..a0d1557 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -145,6 +145,7 @@
     field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
     field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
     field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
+    field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
     field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 72a432e..7787963 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -149,7 +149,6 @@
 filegroup {
     name: "framework-services-net-module-wifi-shared-srcs",
     srcs: [
-        "android/net/DhcpResults.java",
         "android/util/LocalLog.java",
     ],
 }
diff --git a/core/java/android/net/DhcpResults.aidl b/core/java/android/net/DhcpResults.aidl
deleted file mode 100644
index f4db3c3..0000000
--- a/core/java/android/net/DhcpResults.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2012, 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 android.net;
-
-parcelable DhcpResults;
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
deleted file mode 100644
index 82ba156..0000000
--- a/core/java/android/net/DhcpResults.java
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.net;
-
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.net.module.util.InetAddressUtils;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * A simple object for retrieving the results of a DHCP request.
- * Optimized (attempted) for that jni interface
- * TODO: remove this class and replace with other existing constructs
- * @hide
- */
-public final class DhcpResults implements Parcelable {
-    private static final String TAG = "DhcpResults";
-
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public LinkAddress ipAddress;
-
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public InetAddress gateway;
-
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public final ArrayList<InetAddress> dnsServers = new ArrayList<>();
-
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public String domains;
-
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public Inet4Address serverAddress;
-
-    /** Vendor specific information (from RFC 2132). */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public String vendorInfo;
-
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public int leaseDuration;
-
-    /** Link MTU option. 0 means unset. */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public int mtu;
-
-    public String serverHostName;
-
-    @Nullable
-    public String captivePortalApiUrl;
-
-    public DhcpResults() {
-        super();
-    }
-
-    /**
-     * Create a {@link StaticIpConfiguration} based on the DhcpResults.
-     */
-    public StaticIpConfiguration toStaticIpConfiguration() {
-        return new StaticIpConfiguration.Builder()
-                .setIpAddress(ipAddress)
-                .setGateway(gateway)
-                .setDnsServers(dnsServers)
-                .setDomains(domains)
-                .build();
-    }
-
-    public DhcpResults(StaticIpConfiguration source) {
-        if (source != null) {
-            ipAddress = source.getIpAddress();
-            gateway = source.getGateway();
-            dnsServers.addAll(source.getDnsServers());
-            domains = source.getDomains();
-        }
-    }
-
-    /** copy constructor */
-    public DhcpResults(DhcpResults source) {
-        this(source == null ? null : source.toStaticIpConfiguration());
-        if (source != null) {
-            serverAddress = source.serverAddress;
-            vendorInfo = source.vendorInfo;
-            leaseDuration = source.leaseDuration;
-            mtu = source.mtu;
-            serverHostName = source.serverHostName;
-            captivePortalApiUrl = source.captivePortalApiUrl;
-        }
-    }
-
-    /**
-     * @see StaticIpConfiguration#getRoutes(String)
-     * @hide
-     */
-    public List<RouteInfo> getRoutes(String iface) {
-        return toStaticIpConfiguration().getRoutes(iface);
-    }
-
-    /**
-     * Test if this DHCP lease includes vendor hint that network link is
-     * metered, and sensitive to heavy data transfers.
-     */
-    public boolean hasMeteredHint() {
-        if (vendorInfo != null) {
-            return vendorInfo.contains("ANDROID_METERED");
-        } else {
-            return false;
-        }
-    }
-
-    public void clear() {
-        ipAddress = null;
-        gateway = null;
-        dnsServers.clear();
-        domains = null;
-        serverAddress = null;
-        vendorInfo = null;
-        leaseDuration = 0;
-        mtu = 0;
-        serverHostName = null;
-        captivePortalApiUrl = null;
-    }
-
-    @Override
-    public String toString() {
-        StringBuffer str = new StringBuffer(super.toString());
-
-        str.append(" DHCP server ").append(serverAddress);
-        str.append(" Vendor info ").append(vendorInfo);
-        str.append(" lease ").append(leaseDuration).append(" seconds");
-        if (mtu != 0) str.append(" MTU ").append(mtu);
-        str.append(" Servername ").append(serverHostName);
-        if (captivePortalApiUrl != null) {
-            str.append(" CaptivePortalApiUrl ").append(captivePortalApiUrl);
-        }
-
-        return str.toString();
-    }
-
-    @Override
-    public boolean equals(@Nullable Object obj) {
-        if (this == obj) return true;
-
-        if (!(obj instanceof DhcpResults)) return false;
-
-        DhcpResults target = (DhcpResults)obj;
-
-        return toStaticIpConfiguration().equals(target.toStaticIpConfiguration())
-                && Objects.equals(serverAddress, target.serverAddress)
-                && Objects.equals(vendorInfo, target.vendorInfo)
-                && Objects.equals(serverHostName, target.serverHostName)
-                && leaseDuration == target.leaseDuration
-                && mtu == target.mtu
-                && Objects.equals(captivePortalApiUrl, target.captivePortalApiUrl);
-    }
-
-    /**
-     * Implement the Parcelable interface
-     */
-    public static final @android.annotation.NonNull Creator<DhcpResults> CREATOR =
-        new Creator<DhcpResults>() {
-            public DhcpResults createFromParcel(Parcel in) {
-                return readFromParcel(in);
-            }
-
-            public DhcpResults[] newArray(int size) {
-                return new DhcpResults[size];
-            }
-        };
-
-    /** Implement the Parcelable interface */
-    public void writeToParcel(Parcel dest, int flags) {
-        toStaticIpConfiguration().writeToParcel(dest, flags);
-        dest.writeInt(leaseDuration);
-        dest.writeInt(mtu);
-        InetAddressUtils.parcelInetAddress(dest, serverAddress, flags);
-        dest.writeString(vendorInfo);
-        dest.writeString(serverHostName);
-        dest.writeString(captivePortalApiUrl);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    private static DhcpResults readFromParcel(Parcel in) {
-        final StaticIpConfiguration s = StaticIpConfiguration.CREATOR.createFromParcel(in);
-        final DhcpResults dhcpResults = new DhcpResults(s);
-        dhcpResults.leaseDuration = in.readInt();
-        dhcpResults.mtu = in.readInt();
-        dhcpResults.serverAddress = (Inet4Address) InetAddressUtils.unparcelInetAddress(in);
-        dhcpResults.vendorInfo = in.readString();
-        dhcpResults.serverHostName = in.readString();
-        dhcpResults.captivePortalApiUrl = in.readString();
-        return dhcpResults;
-    }
-
-    // Utils for jni population - false on success
-    // Not part of the superclass because they're only used by the JNI iterface to the DHCP daemon.
-    public boolean setIpAddress(String addrString, int prefixLength) {
-        try {
-            Inet4Address addr = (Inet4Address) InetAddresses.parseNumericAddress(addrString);
-            ipAddress = new LinkAddress(addr, prefixLength);
-        } catch (IllegalArgumentException|ClassCastException e) {
-            Log.e(TAG, "setIpAddress failed with addrString " + addrString + "/" + prefixLength);
-            return true;
-        }
-        return false;
-    }
-
-    public boolean setGateway(String addrString) {
-        try {
-            gateway = InetAddresses.parseNumericAddress(addrString);
-        } catch (IllegalArgumentException e) {
-            Log.e(TAG, "setGateway failed with addrString " + addrString);
-            return true;
-        }
-        return false;
-    }
-
-    public boolean addDns(String addrString) {
-        if (TextUtils.isEmpty(addrString) == false) {
-            try {
-                dnsServers.add(InetAddresses.parseNumericAddress(addrString));
-            } catch (IllegalArgumentException e) {
-                Log.e(TAG, "addDns failed with addrString " + addrString);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public LinkAddress getIpAddress() {
-        return ipAddress;
-    }
-
-    public void setIpAddress(LinkAddress ipAddress) {
-        this.ipAddress = ipAddress;
-    }
-
-    public InetAddress getGateway() {
-        return gateway;
-    }
-
-    public void setGateway(InetAddress gateway) {
-        this.gateway = gateway;
-    }
-
-    public List<InetAddress> getDnsServers() {
-        return dnsServers;
-    }
-
-    /**
-     * Add a DNS server to this configuration.
-     */
-    public void addDnsServer(InetAddress server) {
-        dnsServers.add(server);
-    }
-
-    public String getDomains() {
-        return domains;
-    }
-
-    public void setDomains(String domains) {
-        this.domains = domains;
-    }
-
-    public Inet4Address getServerAddress() {
-        return serverAddress;
-    }
-
-    public void setServerAddress(Inet4Address addr) {
-        serverAddress = addr;
-    }
-
-    public int getLeaseDuration() {
-        return leaseDuration;
-    }
-
-    public void setLeaseDuration(int duration) {
-        leaseDuration = duration;
-    }
-
-    public String getVendorInfo() {
-        return vendorInfo;
-    }
-
-    public void setVendorInfo(String info) {
-        vendorInfo = info;
-    }
-
-    public int getMtu() {
-        return mtu;
-    }
-
-    public void setMtu(int mtu) {
-        this.mtu = mtu;
-    }
-
-    public String getCaptivePortalApiUrl() {
-        return captivePortalApiUrl;
-    }
-
-    public void setCaptivePortalApiUrl(String url) {
-        captivePortalApiUrl = url;
-    }
-}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6863a0b..f46f452 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1037,7 +1037,7 @@
          * will also enable {@link StrictMode.ThreadPolicy.Builder#detectUnbufferedIo}.</li>
          * <li>{@link android.provider.DocumentsContract}'s various methods will throw failure
          * exceptions back to the caller instead of returning null.
-         * <li>{@link View#hasFocusable View.hasFocusable} now includes auto-focusable views.</li>
+         * <li>{@link View#hasFocusable() View.hasFocusable} now includes auto-focusable views.</li>
          * <li>{@link android.view.SurfaceView} will no longer always change the underlying
          * Surface object when something about it changes; apps need to look at the current
          * state of the object to determine which things they are interested in have changed.</li>
@@ -1145,6 +1145,14 @@
         public static final int S = 31;
 
         /**
+         * S V2.
+         *
+         * Once more unto the breach, dear friends, once more.
+         *
+         */
+        public static final int S_V2 = 32;
+
+        /**
          * Tiramisu.
          */
         public static final int TIRAMISU = CUR_DEVELOPMENT;
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 61b91dd..13ca133 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -873,7 +873,7 @@
             const char* exceptionToThrow;
             char msg[128];
             // TransactionTooLargeException is a checked exception, only throw from certain methods.
-            // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
+            // TODO(b/28321379): Transaction size is the most common cause for FAILED_TRANSACTION
             //        but it is not the only one.  The Binder driver can return BR_FAILED_REPLY
             //        for other reasons also, such as if the transaction is malformed or
             //        refers to an FD that has been closed.  We should change the driver
@@ -890,8 +890,9 @@
                 exceptionToThrow = (canThrowRemoteException)
                         ? "android/os/DeadObjectException"
                         : "java/lang/RuntimeException";
-                snprintf(msg, sizeof(msg)-1,
-                        "Transaction failed on small parcel; remote process probably died");
+                snprintf(msg, sizeof(msg) - 1,
+                         "Transaction failed on small parcel; remote process probably died, but "
+                         "this could also be caused by running out of binder buffer space");
             }
             jniThrowException(env, exceptionToThrow, msg);
         } break;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index aacf700..5efc4db 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -202,6 +202,8 @@
  */
 static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 1000 * 60 * 5;
 
+static void WaitUntilDirReady(const std::string& target, fail_fn_t fail_fn);
+
 /**
  * A helper class containing accounting information for USAPs.
  */
@@ -1249,7 +1251,11 @@
     auto volPath = StringPrintf("%s/%s", externalPrivateMountPath, ent->d_name);
     auto cePath = StringPrintf("%s/user", volPath.c_str());
     auto dePath = StringPrintf("%s/user_de", volPath.c_str());
+    // Wait until dir user is created.
+    WaitUntilDirReady(cePath.c_str(), fail_fn);
     MountAppDataTmpFs(cePath.c_str(), fail_fn);
+    // Wait until dir user_de is created.
+    WaitUntilDirReady(dePath.c_str(), fail_fn);
     MountAppDataTmpFs(dePath.c_str(), fail_fn);
   }
   closedir(dir);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7f95ca9..111b694 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1945,6 +1945,11 @@
     <permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Allows an application to manage ethernet networks.
+         <p>Not for use by third-party or privileged applications. -->
+    <permission android:name="android.permission.MANAGE_ETHERNET_NETWORKS"
+        android:protectionLevel="signature" />
+
     <!-- ======================================= -->
     <!-- Permissions for short range, peripheral networks -->
     <!-- ======================================= -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index af47b17..fcd1799 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -393,6 +393,7 @@
         <!-- Permission required for CTS test - TrustTestCases -->
         <permission name="android.permission.PROVIDE_TRUST_AGENT" />
         <permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+        <permission name="android.permission.TRUST_LISTENER" />
         <!-- Permissions required for Incremental CTS tests -->
         <permission name="com.android.permission.USE_INSTALLER_V2"/>
         <permission name="android.permission.LOADER_USAGE_STATS"/>
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
index f472d56..e0ce081 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
@@ -358,6 +358,7 @@
         return proxy;
     }
 
+    @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS)
     private void updateConfiguration(
             @NonNull String iface,
             @NonNull EthernetNetworkUpdateRequest request,
@@ -372,6 +373,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS)
     private void connectNetwork(
             @NonNull String iface,
             @Nullable @CallbackExecutor Executor executor,
@@ -385,6 +387,7 @@
         }
     }
 
+    @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS)
     private void disconnectNetwork(
             @NonNull String iface,
             @Nullable @CallbackExecutor Executor executor,
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index 24bc91d..d05370f 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -28,6 +28,11 @@
         "src/com/android/server/net/NetworkStats*.java",
         "src/com/android/server/net/BpfInterfaceMapUpdater.java",
         "src/com/android/server/net/InterfaceMapValue.java",
+        "src/com/android/server/net/CookieTagMapKey.java",
+        "src/com/android/server/net/CookieTagMapValue.java",
+        "src/com/android/server/net/StatsMapKey.java",
+        "src/com/android/server/net/StatsMapValue.java",
+        "src/com/android/server/net/UidStatsMapKey.java",
     ],
     path: "src",
     visibility: [
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java
new file mode 100644
index 0000000..443e5b3
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for cookie tag map.
+ */
+public class CookieTagMapKey extends Struct {
+    @Field(order = 0, type = Type.S64)
+    public final long socketCookie;
+
+    public CookieTagMapKey(final long socketCookie) {
+        this.socketCookie = socketCookie;
+    }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java
new file mode 100644
index 0000000..93b9195
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Value for cookie tag map.
+ */
+public class CookieTagMapValue extends Struct {
+    @Field(order = 0, type = Type.U32)
+    public final long uid;
+
+    @Field(order = 1, type = Type.U32)
+    public final long tag;
+
+    public CookieTagMapValue(final long uid, final long tag) {
+        this.uid = uid;
+        this.tag = tag;
+    }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java
new file mode 100644
index 0000000..ea8d836
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for both stats maps.
+ */
+public class StatsMapKey extends Struct {
+    @Field(order = 0, type = Type.U32)
+    public final long uid;
+
+    @Field(order = 1, type = Type.U32)
+    public final long tag;
+
+    @Field(order = 2, type = Type.U32)
+    public final long counterSet;
+
+    @Field(order = 3, type = Type.U32)
+    public final long ifaceIndex;
+
+    public StatsMapKey(final long uid, final long tag, final long counterSet,
+            final long ifaceIndex) {
+        this.uid = uid;
+        this.tag = tag;
+        this.counterSet = counterSet;
+        this.ifaceIndex = ifaceIndex;
+    }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java
new file mode 100644
index 0000000..48f26ce
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Value used for both stats maps and uid stats map.
+ */
+public class StatsMapValue extends Struct {
+    @Field(order = 0, type = Type.U63)
+    public final long rxPackets;
+
+    @Field(order = 1, type = Type.U63)
+    public final long rxBytes;
+
+    @Field(order = 2, type = Type.U63)
+    public final long txPackets;
+
+    @Field(order = 3, type = Type.U63)
+    public final long txBytes;
+
+    public StatsMapValue(final long rxPackets, final long rxBytes, final long txPackets,
+            final long txBytes) {
+        this.rxPackets = rxPackets;
+        this.rxBytes = rxBytes;
+        this.txPackets = txPackets;
+        this.txBytes = txBytes;
+    }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java
new file mode 100644
index 0000000..2849f94
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for uid stats map.
+ */
+public class UidStatsMapKey extends Struct {
+    @Field(order = 0, type = Type.U32)
+    public final long uid;
+
+    public UidStatsMapKey(final long uid) {
+        this.uid = uid;
+    }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7724c1a..074d85c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -536,6 +536,7 @@
     <!-- Permissions required for CTS test - TrustTestCases -->
     <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+    <uses-permission android:name="android.permission.TRUST_LISTENER" />
 
     <!-- Permission required for CTS test - CtsGameManagerTestCases -->
     <uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 48f5b51..a0575cf 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -484,7 +484,9 @@
         HashMap<String, Long> timestamps = readTimestamps();
         try {
             if (proto) {
-                db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
+                if (recordFileTimestamp(tombstone, timestamps)) {
+                    db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
+                }
             } else {
                 final String headers = getBootHeadersToLogAndUpdate();
                 addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
@@ -526,16 +528,10 @@
         if (db == null || !db.isTagEnabled(tag)) return;  // Logging disabled
 
         File file = new File(filename);
-        long fileTime = file.lastModified();
-        if (fileTime <= 0) return;  // File does not exist
-
-        if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) {
-            return;  // Already logged this particular file
+        if (!recordFileTimestamp(file, timestamps)) {
+            return;
         }
 
-        timestamps.put(filename, fileTime);
-
-
         String fileContents = FileUtils.readTextFile(file, maxSize, TAG_TRUNCATED);
         String text = headers + fileContents + footers;
         // Create an additional report for system server native crashes, with a special tag.
@@ -548,6 +544,19 @@
         addTextToDropBox(db, tag, text, filename, maxSize);
     }
 
+    private static boolean recordFileTimestamp(File file, HashMap<String, Long> timestamps) {
+        final long fileTime = file.lastModified();
+        if (fileTime <= 0) return false;  // File does not exist
+
+        final String filename = file.getPath();
+        if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) {
+            return false;  // Already logged this particular file
+        }
+
+        timestamps.put(filename, fileTime);
+        return true;
+    }
+
     private static void addTextToDropBox(DropBoxManager db, String tag, String text,
             String filename, int maxSize) {
         Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 38df5f8..a0c5aa3 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -639,7 +639,7 @@
                         clipboard.primaryClipListeners.getBroadcastItem(i)
                                 .dispatchPrimaryClipChanged();
                     }
-                } catch (RemoteException e) {
+                } catch (RemoteException | SecurityException e) {
                     // The RemoteCallbackList will take care of removing
                     // the dead object for us.
                 }
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index c30d761..21256d8 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -20,17 +20,20 @@
     <uses-sdk android:minSdkVersion="30"/>
 
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
     <application android:label="SilkFX"
          android:theme="@android:style/Theme.Material">
 
         <activity android:name=".Main"
              android:label="SilkFX Demos"
+             android:banner="@drawable/background1"
              android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.DEFAULT"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
             </intent-filter>
         </activity>
 
@@ -41,13 +44,16 @@
 
         <activity android:name=".materials.GlassActivity"
             android:label="Glass Examples"
-            android:banner="@drawable/background1"
             android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
             </intent-filter>
         </activity>
 
+        <activity android:name=".materials.BackgroundBlurActivity"
+            android:theme="@style/Theme.BackgroundBlurTheme"
+            android:exported="true">
+        </activity>
+
     </application>
 </manifest>
diff --git a/tests/SilkFX/res/drawable/background_blur_drawable.xml b/tests/SilkFX/res/drawable/background_blur_drawable.xml
new file mode 100644
index 0000000..173ca99
--- /dev/null
+++ b/tests/SilkFX/res/drawable/background_blur_drawable.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+           android:shape="rectangle">
+    <solid android:color="#20FFFFFF"/>
+    <corners android:radius="10dp"/>
+</shape>
diff --git a/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
new file mode 100644
index 0000000..bd8942d
--- /dev/null
+++ b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="10dp"/>
+</shape>
diff --git a/tests/SilkFX/res/layout/activity_background_blur.xml b/tests/SilkFX/res/layout/activity_background_blur.xml
new file mode 100644
index 0000000..f13c088
--- /dev/null
+++ b/tests/SilkFX/res/layout/activity_background_blur.xml
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/background"
+    android:layout_width="390dp"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:padding="15dp"
+    android:orientation="vertical"
+    tools:context=".materials.BackgroundBlurActivity">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:padding="10dp"
+        android:textColor="#ffffffff"
+        android:text="Hello blurry world!"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textColor="#ffffffff"
+            android:text="Background blur"/>
+
+        <SeekBar
+            android:id="@+id/set_background_blur"
+            android:min="0"
+            android:max="300"
+            android:layout_width="160dp"
+            android:layout_height="wrap_content"/>
+        <TextView
+            android:id="@+id/background_blur_radius"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="#ffffffff"
+            android:ems="3"
+            android:gravity="center"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:text="TODO"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textColor="#ffffffff"
+            android:text="Background alpha"/>
+
+        <SeekBar
+            android:id="@+id/set_background_alpha"
+            android:min="0"
+            android:max="100"
+            android:layout_width="160dp"
+            android:layout_height="wrap_content" />
+        <TextView
+            android:id="@+id/background_alpha"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="#ffffffff"
+            android:ems="3"
+            android:gravity="center"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:text="TODO"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textColor="#ffffffff"
+            android:text="Blur behind"/>
+
+        <SeekBar
+            android:id="@+id/set_blur_behind"
+            android:min="0"
+            android:max="300"
+            android:layout_width="160dp"
+            android:layout_height="wrap_content" />
+        <TextView
+            android:id="@+id/blur_behind_radius"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:textColor="#ffffffff"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:ems="3"
+            android:text="TODO"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textColor="#ffffffff"
+            android:text="Dim amount"/>
+
+        <SeekBar
+            android:id="@+id/set_dim_amount"
+            android:min="0"
+            android:max="100"
+            android:layout_width="160dp"
+            android:layout_height="wrap_content" />
+        <TextView
+            android:id="@+id/dim_amount"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:textColor="#ffffffff"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:ems="3"
+            android:text="TODO"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="5dp"
+        android:orientation="vertical"
+        android:gravity="center">
+
+        <Button
+            android:id="@+id/toggle_blur_enabled"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Disable blur"
+            android:onClick="toggleForceBlurDisabled"/>
+
+        <Button
+            android:id="@+id/toggle_battery_saving_mode"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="TODO"
+            android:onClick="toggleBatterySavingMode"/>
+    </LinearLayout>
+    <requestFocus/>
+
+</LinearLayout>
diff --git a/tests/SilkFX/res/values/style.xml b/tests/SilkFX/res/values/style.xml
new file mode 100644
index 0000000..66edbb5
--- /dev/null
+++ b/tests/SilkFX/res/values/style.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<!-- Styles for immersive actions UI. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="Theme.BackgroundBlurTheme" parent= "Theme.AppCompat.Dialog">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBlurBehindEnabled">true</item>
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:windowElevation">0dp</item>
+        <item name="buttonStyle">@style/AppTheme.Button</item>
+        <item name="colorAccent">#bbffffff</item>
+    </style>
+    <style name="AppTheme.Button" parent="Widget.AppCompat.Button">
+        <item name="android:textColor">#ffffffff</item>
+    </style>
+
+</resources>
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
index 9ed8d2f..7132ae8 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -30,6 +30,7 @@
 import com.android.test.silkfx.app.EXTRA_TITLE
 import com.android.test.silkfx.hdr.GlowActivity
 import com.android.test.silkfx.materials.GlassActivity
+import com.android.test.silkfx.materials.BackgroundBlurActivity
 import kotlin.reflect.KClass
 
 class Demo(val name: String, val makeIntent: (Context) -> Intent) {
@@ -51,7 +52,8 @@
                 Demo("Blingy Notifications", R.layout.bling_notifications)
         )),
         DemoGroup("Materials", listOf(
-                Demo("Glass", GlassActivity::class)
+                Demo("Glass", GlassActivity::class),
+                Demo("Background Blur", BackgroundBlurActivity::class)
         ))
 )
 
@@ -126,4 +128,4 @@
 
         AllDemos.forEachIndexed { index, _ -> list.expandGroup(index) }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
new file mode 100644
index 0000000..9d17d38
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 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.test.silkfx.materials
+
+import android.app.Activity
+import android.content.Intent
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.PaintDrawable
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.provider.Settings
+import android.util.TypedValue
+import android.view.View
+import android.view.WindowManager
+import android.widget.ImageView
+import android.widget.SeekBar
+import android.widget.Switch
+import android.widget.TextView
+import com.android.test.silkfx.R
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable
+import android.widget.LinearLayout
+import android.widget.Button
+
+import android.view.ViewRootImpl
+
+class BackgroundBlurActivity : Activity(), SeekBar.OnSeekBarChangeListener  {
+    var mBackgroundDrawable = PaintDrawable(Color.WHITE)
+    var mBackgroundBlurRadius = 50
+    var mAlphaWithBlur = 0.2f
+    var mAlphaNoBlur = 0.5f
+
+    var mBlurBehindRadius = 10
+    var mDimAmountWithBlur = 0.2f
+    var mDimAmountNoBlur = 0.2f
+
+    var mBlurForceDisabled = false
+    var mBatterySavingModeOn = false
+
+    lateinit var blurBackgroundSeekBar: SeekBar
+    lateinit var backgroundAlphaSeekBar : SeekBar
+    lateinit var blurBehindSeekBar : SeekBar
+    lateinit var dimAmountSeekBar : SeekBar
+
+    val blurEnabledListener = { enabled : Boolean ->
+        blurBackgroundSeekBar.setProgress(mBackgroundBlurRadius)
+        blurBehindSeekBar.setProgress(mBlurBehindRadius)
+
+        if (enabled) {
+            setBackgroundBlur(mBackgroundBlurRadius)
+            setBackgroundColorAlpha(mAlphaWithBlur)
+
+            setBlurBehind(mBlurBehindRadius)
+            setDimAmount(mDimAmountWithBlur)
+
+            backgroundAlphaSeekBar.setProgress((mAlphaWithBlur * 100).toInt())
+            dimAmountSeekBar.setProgress((mDimAmountWithBlur * 100).toInt())
+        } else {
+            setBackgroundColorAlpha(mAlphaNoBlur)
+            setDimAmount(mDimAmountNoBlur)
+
+            backgroundAlphaSeekBar.setProgress((mAlphaNoBlur * 100).toInt())
+            dimAmountSeekBar.setProgress((mDimAmountNoBlur * 100).toInt())
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_background_blur)
+
+        window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
+        window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+
+        mBackgroundDrawable.setCornerRadius(30f)
+        window.setBackgroundDrawable(mBackgroundDrawable)
+
+        mBatterySavingModeOn =
+            Settings.Global.getInt(getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) == 1
+        setBatterySavingModeOn(mBatterySavingModeOn)
+
+        blurBackgroundSeekBar = requireViewById(R.id.set_background_blur)
+        backgroundAlphaSeekBar = requireViewById(R.id.set_background_alpha)
+        blurBehindSeekBar = requireViewById(R.id.set_blur_behind)
+        dimAmountSeekBar = requireViewById(R.id.set_dim_amount)
+
+        arrayOf(blurBackgroundSeekBar, backgroundAlphaSeekBar, blurBehindSeekBar, dimAmountSeekBar)
+                .forEach {
+                    it.setOnSeekBarChangeListener(this)
+                }
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+        getWindowManager().addCrossWindowBlurEnabledListener(blurEnabledListener)
+    }
+
+    override fun onDetachedFromWindow() {
+        super.onDetachedFromWindow()
+        getWindowManager().removeCrossWindowBlurEnabledListener(blurEnabledListener)
+    }
+
+    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+        when (seekBar) {
+            blurBackgroundSeekBar -> setBackgroundBlur(progress)
+            backgroundAlphaSeekBar -> setBackgroundColorAlpha(progress / 100.0f)
+            blurBehindSeekBar -> setBlurBehind(progress)
+            dimAmountSeekBar -> setDimAmount(progress / 100.0f)
+            else -> throw IllegalArgumentException("Unknown seek bar")
+        }
+    }
+
+    override fun onStartTrackingTouch(seekBar: SeekBar?) {}
+    override fun onStopTrackingTouch(seekBar: SeekBar?) {}
+
+    fun setBlurDisabled(disabled: Boolean) {
+        mBlurForceDisabled = disabled
+        Settings.Global.putInt(getContentResolver(), Settings.Global.DISABLE_WINDOW_BLURS,
+                if (mBlurForceDisabled) 1 else 0)
+        (findViewById(R.id.toggle_blur_enabled) as Button)
+                .setText(if (mBlurForceDisabled) "Enable blurs" else "Disable blurs")
+    }
+
+    fun toggleForceBlurDisabled(v: View) {
+        setBlurDisabled(!mBlurForceDisabled)
+    }
+
+    fun setBackgroundBlur(radius: Int) {
+        mBackgroundBlurRadius = radius
+        (findViewById(R.id.background_blur_radius) as TextView).setText(radius.toString())
+        window.setBackgroundBlurRadius(mBackgroundBlurRadius)
+    }
+
+    fun setBlurBehind(radius: Int) {
+        mBlurBehindRadius = radius
+        (findViewById(R.id.blur_behind_radius) as TextView).setText(radius.toString())
+        window.getAttributes().setBlurBehindRadius(mBlurBehindRadius)
+        window.setAttributes(window.getAttributes())
+    }
+
+    fun setDimAmount(amount: Float) {
+        if (getWindowManager().isCrossWindowBlurEnabled()) {
+            mDimAmountWithBlur = amount
+        } else {
+            mDimAmountNoBlur = amount
+        }
+        (findViewById(R.id.dim_amount) as TextView).setText("%.2f".format(amount))
+        window.getAttributes().dimAmount = amount
+        window.setAttributes(window.getAttributes())
+    }
+
+    fun setBatterySavingModeOn(on: Boolean) {
+        mBatterySavingModeOn = on
+        Settings.Global.putInt(getContentResolver(),
+            Settings.Global.LOW_POWER_MODE, if (on) 1 else 0)
+        (findViewById(R.id.toggle_battery_saving_mode) as Button).setText(
+            if (on) "Exit low power mode" else "Enter low power mode")
+    }
+
+    fun toggleBatterySavingMode(v: View) {
+        setBatterySavingModeOn(!mBatterySavingModeOn)
+    }
+
+    fun setBackgroundColorAlpha(alpha: Float) {
+        if (getWindowManager().isCrossWindowBlurEnabled()) {
+            mAlphaWithBlur = alpha
+        } else {
+            mAlphaNoBlur = alpha
+        }
+        (findViewById(R.id.background_alpha) as TextView).setText("%.2f".format(alpha))
+        mBackgroundDrawable.setAlpha((alpha * 255f).toInt())
+        getWindowManager().updateViewLayout(window.getDecorView(), window.getAttributes())
+    }
+}