Sync tieProfileLockIfNecessary() with internal main am: ada6ce9436

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2871508

Change-Id: I09431365f49d72fad8d0b755de3026de5f6380df
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index 7716055..91b796a 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -218,13 +218,6 @@
     @SystemApi
     public static native int getPidIfSharable();
 
-    /**
-     * Return true if HIDL is supported on this device and false if not.
-     *
-     * @hide
-     */
-    public static native boolean isHidlSupported();
-
     /** @hide */
     public HidlSupport() {}
 }
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index bc19655..feed208 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -18,7 +18,6 @@
 
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.util.Log;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -79,17 +78,6 @@
             String iface,
             String serviceName)
         throws RemoteException, NoSuchElementException {
-        if (!HidlSupport.isHidlSupported()
-                && (iface.equals("android.hidl.manager@1.0::IServiceManager")
-                        || iface.equals("android.hidl.manager@1.1::IServiceManager")
-                        || iface.equals("android.hidl.manager@1.2::IServiceManager"))) {
-            Log.i(
-                    TAG,
-                    "Replacing Java hwservicemanager with a fake HwNoService"
-                            + " because HIDL is not supported on this device.");
-            return new HwNoService();
-        }
-
         return getService(iface, serviceName, false /* retry */);
     }
     /**
diff --git a/core/java/android/os/HwNoService.java b/core/java/android/os/HwNoService.java
deleted file mode 100644
index 117c3ad..0000000
--- a/core/java/android/os/HwNoService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2023 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.os;
-
-/**
- * A fake hwservicemanager that is used locally when HIDL isn't supported on the device.
- *
- * @hide
- */
-final class HwNoService implements IHwBinder, IHwInterface {
-    /** @hide */
-    @Override
-    public void transact(int code, HwParcel request, HwParcel reply, int flags) {}
-
-    /** @hide */
-    @Override
-    public IHwInterface queryLocalInterface(String descriptor) {
-        return new HwNoService();
-    }
-
-    /** @hide */
-    @Override
-    public boolean linkToDeath(DeathRecipient recipient, long cookie) {
-        return true;
-    }
-
-    /** @hide */
-    @Override
-    public boolean unlinkToDeath(DeathRecipient recipient) {
-        return true;
-    }
-
-    /** @hide */
-    @Override
-    public IHwBinder asBinder() {
-        return this;
-    }
-}
diff --git a/core/java/android/service/persistentdata/OWNERS b/core/java/android/service/persistentdata/OWNERS
new file mode 100644
index 0000000..6dfb888
--- /dev/null
+++ b/core/java/android/service/persistentdata/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pdb/OWNERS
diff --git a/core/jni/android_os_HidlSupport.cpp b/core/jni/android_os_HidlSupport.cpp
index 3e51e93..e3602d8 100644
--- a/core/jni/android_os_HidlSupport.cpp
+++ b/core/jni/android_os_HidlSupport.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <hidl/HidlTransportSupport.h>
-#include <hidl/ServiceManagement.h>
 #include <nativehelper/JNIHelp.h>
 
 #include "core_jni_helpers.h"
@@ -25,13 +24,8 @@
     return android::hardware::details::getPidIfSharable();
 }
 
-static jboolean android_os_HidlSupport_isHidlSupported(JNIEnv*, jclass) {
-    return android::hardware::isHidlSupported();
-}
-
 static const JNINativeMethod gHidlSupportMethods[] = {
-        {"getPidIfSharable", "()I", (void*)android_os_HidlSupport_getPidIfSharable},
-        {"isHidlSupported", "()Z", (void*)android_os_HidlSupport_isHidlSupported},
+    {"getPidIfSharable", "()I", (void*)android_os_HidlSupport_getPidIfSharable},
 };
 
 const char* const kHidlSupportPathName = "android/os/HidlSupport";
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 2b5b8f7..3377362 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -63,6 +63,7 @@
   std::optional<std::pair<char*, char*>> readLine(FailFn fail_fn) {
     char* result = mBuffer + mNext;
     while (true) {
+      // We have scanned up to, but not including mNext for this line's newline.
       if (mNext == mEnd) {
         if (mEnd == MAX_COMMAND_BYTES) {
           return {};
@@ -89,7 +90,7 @@
       } else {
         mNext = nl - mBuffer + 1;
         if (--mLinesLeft < 0) {
-          fail_fn("ZygoteCommandBuffer.readLine attempted to read past mEnd of command");
+          fail_fn("ZygoteCommandBuffer.readLine attempted to read past end of command");
         }
         return std::make_pair(result, nl);
       }
@@ -125,8 +126,8 @@
     mEnd += lineLen + 1;
   }
 
-  // Clear mBuffer, start reading new command, return the number of arguments, leaving mBuffer
-  // positioned at the beginning of first argument. Return 0 on EOF.
+  // Start reading new command, return the number of arguments, leaving mBuffer positioned at the
+  // beginning of first argument. Return 0 on EOF.
   template<class FailFn>
   int getCount(FailFn fail_fn) {
     mLinesLeft = 1;
@@ -451,11 +452,14 @@
             (CREATE_ERROR("Write unexpectedly returned short: %d < 5", res));
       }
     }
-    // Clear buffer and get count from next command.
-    n_buffer->clear();
     for (;;) {
+      // Clear buffer and get count from next command.
+      n_buffer->clear();
       // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect.
       int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */));
+      if (poll_res < 0) {
+        fail_fn_z(CREATE_ERROR("Poll failed: %d: %s", errno, strerror(errno)));
+      }
       if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) {
         if (n_buffer->getCount(fail_fn_z) != 0) {
           break;
diff --git a/media/OWNERS b/media/OWNERS
index 4a6648e..994a7b8 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -21,7 +21,6 @@
 include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
 
 # SEO
-sungsoo@google.com
 
 # SEA/KIR/BVE
 jtinker@google.com
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index bbe5e06..058c5be 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -2,7 +2,6 @@
 fgoldfain@google.com
 elaurent@google.com
 lajos@google.com
-sungsoo@google.com
 jmtrivi@google.com
 
 # go/android-fwk-media-solutions for info on areas of ownership.
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bedfd1c..b555a52 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -37,7 +37,6 @@
 import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
-
 import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
 import static com.android.server.utils.EventLogger.Event.ALOGE;
 import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -73,7 +72,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -263,6 +261,8 @@
     private final SettingsAdapter mSettings;
     private final AudioPolicyFacade mAudioPolicy;
 
+    private final MusicFxHelper mMusicFxHelper;
+
     /** Debug audio mode */
     protected static final boolean DEBUG_MODE = false;
 
@@ -403,9 +403,15 @@
     private static final int MSG_CONFIGURATION_CHANGED = 54;
     private static final int MSG_BROADCAST_MASTER_MUTE = 55;
 
-    /** Messages handled by the {@link SoundDoseHelper}. */
+    /**
+     * Messages handled by the {@link SoundDoseHelper}, do not exceed
+     * {@link MUSICFX_HELPER_MSG_START}.
+     */
     /*package*/ static final int SAFE_MEDIA_VOLUME_MSG_START = 1000;
 
+    /** Messages handled by the {@link MusicFxHelper}. */
+    /*package*/ static final int MUSICFX_HELPER_MSG_START = 1100;
+
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -1268,6 +1274,8 @@
                 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
         queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER,
                 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+
+        mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
     }
 
     private void initVolumeStreamStates() {
@@ -4946,7 +4954,7 @@
     private void setMasterMuteInternalNoCallerCheck(
             boolean mute, int flags, int userId, String eventSource) {
         if (DEBUG_VOL) {
-            Log.d(TAG, TextUtils.formatSimple("Master mute %s, %d, user=%d from %s",
+            Log.d(TAG, TextUtils.formatSimple("Master mute %s, flags 0x%x, userId=%d from %s",
                     mute, flags, userId, eventSource));
         }
 
@@ -9409,11 +9417,21 @@
                     onConfigurationChanged();
                     break;
 
+                case MusicFxHelper.MSG_EFFECT_CLIENT_GONE:
+                    mMusicFxHelper.handleMessage(msg);
+                    break;
+
+                case SoundDoseHelper.MSG_CONFIGURE_SAFE_MEDIA:
+                case SoundDoseHelper.MSG_CONFIGURE_SAFE_MEDIA_FORCED:
+                case SoundDoseHelper.MSG_PERSIST_SAFE_VOLUME_STATE:
+                case SoundDoseHelper.MSG_PERSIST_MUSIC_ACTIVE_MS:
+                case SoundDoseHelper.MSG_PERSIST_CSD_VALUES:
+                case SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION:
+                    mSoundDoseHelper.handleMessage(msg);
+                    break;
+
                 default:
-                    if (msg.what >= SAFE_MEDIA_VOLUME_MSG_START) {
-                        // msg could be for the SoundDoseHelper
-                        mSoundDoseHelper.handleMessage(msg);
-                    }
+                    Log.e(TAG, "Unsupported msgId " + msg.what);
             }
         }
     }
@@ -9648,7 +9666,7 @@
                 }
             } else if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) ||
                     action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) {
-                handleAudioEffectBroadcast(context, intent);
+                mMusicFxHelper.handleAudioEffectBroadcast(context, intent);
             } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
                 final int[] suspendedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                 final String[] suspendedPackages =
@@ -9703,27 +9721,6 @@
         }
     } // end class AudioServiceUserRestrictionsListener
 
-    private void handleAudioEffectBroadcast(Context context, Intent intent) {
-        String target = intent.getPackage();
-        if (target != null) {
-            Log.w(TAG, "effect broadcast already targeted to " + target);
-            return;
-        }
-        intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
-        // TODO this should target a user-selected panel
-        List<ResolveInfo> ril = context.getPackageManager().queryBroadcastReceivers(
-                intent, 0 /* flags */);
-        if (ril != null && ril.size() != 0) {
-            ResolveInfo ri = ril.get(0);
-            if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) {
-                intent.setPackage(ri.activityInfo.packageName);
-                context.sendBroadcastAsUser(intent, UserHandle.ALL);
-                return;
-            }
-        }
-        Log.w(TAG, "couldn't find receiver package for effect intent");
-    }
-
     private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) {
         PackageManager pm = mContext.getPackageManager();
         // Find the home activity of the user. It should not be killed to avoid expensive restart,
@@ -13178,6 +13175,11 @@
         return mDeviceBroker.getDeviceSensorUuid(device);
     }
 
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    MusicFxHelper getMusicFxHelper() {
+        return mMusicFxHelper;
+    }
+
     //======================
     // misc
     //======================
diff --git a/services/core/java/com/android/server/audio/MusicFxHelper.java b/services/core/java/com/android/server/audio/MusicFxHelper.java
new file mode 100644
index 0000000..85b3b49
--- /dev/null
+++ b/services/core/java/com/android/server/audio/MusicFxHelper.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2023 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.audio;
+
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
+import static com.android.server.audio.AudioService.MUSICFX_HELPER_MSG_START;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
+import android.app.IUidObserver;
+import android.app.UidObserver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.media.AudioManager;
+import android.media.audiofx.AudioEffect;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.audio.AudioService.AudioHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MusicFx management.
+ */
+public class MusicFxHelper {
+    private static final String TAG = "AS.MusicFxHelper";
+
+    @NonNull private final Context mContext;
+
+    @NonNull private final AudioHandler mAudioHandler;
+
+    // Synchronization UidSessionMap access between UidObserver and AudioServiceBroadcastReceiver.
+    private final Object mClientUidMapLock = new Object();
+
+    private final String mPackageName = this.getClass().getPackage().getName();
+
+    private final String mMusicFxPackageName = "com.android.musicfx";
+
+    /*package*/ static final int MSG_EFFECT_CLIENT_GONE = MUSICFX_HELPER_MSG_START + 1;
+
+    // The binder token identifying the UidObserver registration.
+    private IBinder mUidObserverToken = null;
+
+    // Package name and list of open audio sessions for this package
+    private static class PackageSessions {
+        String mPackageName;
+        List<Integer> mSessions;
+    }
+
+    /*
+     * Override of SparseArray class to add bind/unbind and UID observer in the put/remove methods.
+     *
+     * put:
+     *  - the first key/value set put into MySparseArray will trigger a procState bump (bindService)
+     *  - if no valid observer token exist, will call registerUidObserver for put
+     *  - for each new uid put into array, it will be added to uid observer list
+     *
+     * remove:
+     *  - for each uid removed from array, it will be removed from uid observer list as well
+     *  - if it's the last uid in array, no more MusicFx procState bump (unbindService), uid
+     *    observer will also be removed, and observer token reset to null
+     */
+    private class MySparseArray extends SparseArray<PackageSessions> {
+        private final String mMusicFxPackageName = "com.android.musicfx";
+
+        @RequiresPermission(anyOf = {
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                android.Manifest.permission.INTERACT_ACROSS_USERS,
+                android.Manifest.permission.INTERACT_ACROSS_PROFILES
+        })
+        @Override
+        public void put(int uid, PackageSessions pkgSessions) {
+            if (size() == 0) {
+                int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+                try {
+                    procState = ActivityManager.getService().getPackageProcessState(
+                            mMusicFxPackageName, mPackageName);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException with getPackageProcessState: " + e);
+                }
+                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                    Intent bindIntent = new Intent().setClassName(mMusicFxPackageName,
+                            "com.android.musicfx.KeepAliveService");
+                    mContext.bindServiceAsUser(
+                            bindIntent, mMusicFxBindConnection, Context.BIND_AUTO_CREATE,
+                            UserHandle.of(getCurrentUserId()));
+                    Log.i(TAG, "bindService to " + mMusicFxPackageName);
+                }
+
+                Log.i(TAG, mMusicFxPackageName + " procState " + procState);
+            }
+            try {
+                if (mUidObserverToken == null) {
+                    mUidObserverToken = ActivityManager.getService().registerUidObserverForUids(
+                            mEffectUidObserver, ActivityManager.UID_OBSERVER_GONE,
+                            ActivityManager.PROCESS_STATE_UNKNOWN, mPackageName,
+                            new int[]{uid});
+                    Log.i(TAG, "registered to observer with UID " + uid);
+                } else if (get(uid) == null) { // addUidToObserver if this is a new UID
+                    ActivityManager.getService().addUidToObserver(mUidObserverToken, mPackageName,
+                            uid);
+                    Log.i(TAG, " UID " + uid + " add to observer");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException with UID observer add/register: " + e);
+            }
+
+            super.put(uid, pkgSessions);
+        }
+
+        @Override
+        public void remove(int uid) {
+            if (get(uid) != null) {
+                try {
+                    ActivityManager.getService().removeUidFromObserver(mUidObserverToken,
+                            mPackageName, uid);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException with removeUidFromObserver: " + e);
+                }
+            }
+
+            super.remove(uid);
+
+            // stop foreground service delegate and unregister UID observers with the last UID
+            if (size() == 0) {
+                try {
+                    ActivityManager.getService().unregisterUidObserver(mEffectUidObserver);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException with unregisterUidObserver: " + e);
+                }
+                mUidObserverToken = null;
+                mContext.unbindService(mMusicFxBindConnection);
+                Log.i(TAG, "last session closed, unregister UID observer, and unbind "
+                        + mMusicFxPackageName);
+            }
+        }
+    }
+
+    // Hashmap of UID and list of open sessions for this UID.
+    @GuardedBy("mClientUidMapLock")
+    private MySparseArray mClientUidSessionMap = new MySparseArray();
+
+    // UID observer for effect MusicFx clients
+    private final IUidObserver mEffectUidObserver = new UidObserver() {
+        @Override public void onUidGone(int uid, boolean disabled) {
+            Log.w(TAG, " send MSG_EFFECT_CLIENT_GONE");
+            mAudioHandler.sendMessageAtTime(
+                    mAudioHandler.obtainMessage(MSG_EFFECT_CLIENT_GONE,
+                            uid /* arg1 */, 0 /* arg2 */,
+                            null /* obj */), 0 /* delay */);
+        }
+    };
+
+    // BindService connection implementation, we don't need any implementation now
+    private ServiceConnection mMusicFxBindConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.d(TAG, " service connected to " + name);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.d(TAG, " service disconnected from " + name);
+        }
+    };
+
+    MusicFxHelper(@NonNull Context context, @NonNull AudioHandler audioHandler) {
+        mContext = context;
+        mAudioHandler = audioHandler;
+    }
+
+    /**
+     * Handle the broadcast {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
+     * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
+     *
+     * Only intents without target application package {@link android.content.Intent#getPackage}
+     * will be handled by the MusicFxHelper, all intents handled and forwarded by MusicFxHelper
+     * will have the target application package.
+     *
+     * If the intent is {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION}:
+     *  - If the MusicFx process is not running, call bindServiceAsUser with AUTO_CREATE to create.
+     *  - If this is the first audio session of MusicFx, call set foreground service delegate.
+     *  - If this is the first audio session for a given UID, add the UID into observer.
+     *
+     * If the intent is {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION}
+     *  - The KeepAliveService of MusicFx will be unbound, and MusicFx will not be foreground
+     *    delegated anymore if the last session of the last package was closed.
+     *  - The Uid Observer will be removed when the last session of a package was closed.
+     */
+    @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public void handleAudioEffectBroadcast(Context context, Intent intent) {
+        String target = intent.getPackage();
+        if (target != null) {
+            Log.w(TAG, "effect broadcast already targeted to " + target);
+            return;
+        }
+        final PackageManager pm = context.getPackageManager();
+        // TODO this should target a user-selected panel
+        List<ResolveInfo> ril = pm.queryBroadcastReceivers(intent, 0 /* flags */);
+        if (ril != null && ril.size() != 0) {
+            ResolveInfo ri = ril.get(0);
+            final String senderPackageName = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME);
+            try {
+                if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) {
+                    final int senderUid = pm.getPackageUidAsUser(senderPackageName,
+                            PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId());
+                    intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+                    intent.setPackage(ri.activityInfo.packageName);
+                    if (setMusicFxServiceWithObserver(intent, senderUid, senderPackageName)) {
+                        context.sendBroadcastAsUser(intent, UserHandle.ALL);
+                    }
+                    return;
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Not able to find UID from package: " + senderPackageName + " error: "
+                        + e);
+            }
+        }
+        Log.w(TAG, "couldn't find receiver package for effect intent");
+    }
+
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES
+    })
+    @GuardedBy("mClientUidMapLock")
+    private boolean handleAudioEffectSessionOpen(
+            int senderUid, String senderPackageName, int sessionId) {
+        Log.d(TAG, senderPackageName + " UID " + senderUid + " open MusicFx session " + sessionId);
+
+        PackageSessions pkgSessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
+        if (pkgSessions != null && pkgSessions.mSessions != null) {
+            if (pkgSessions.mSessions.contains(sessionId)) {
+                Log.e(TAG, "Audio session " + sessionId + " already open for UID: "
+                        + senderUid + ", package: " + senderPackageName + ", abort");
+                return false;
+            }
+            if (pkgSessions.mPackageName != senderPackageName) {
+                Log.w(TAG, "Inconsistency package names for UID open: " + senderUid + " prev: "
+                        + pkgSessions.mPackageName + ", now: " + senderPackageName);
+                return false;
+            }
+        } else {
+            // first session for this UID, create a new Package/Sessions pair
+            pkgSessions = new PackageSessions();
+            pkgSessions.mSessions = new ArrayList();
+            pkgSessions.mPackageName = senderPackageName;
+        }
+
+        pkgSessions.mSessions.add(Integer.valueOf(sessionId));
+        mClientUidSessionMap.put(Integer.valueOf(senderUid), pkgSessions);
+        return true;
+    }
+
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES
+    })
+    @GuardedBy("mClientUidMapLock")
+    private boolean handleAudioEffectSessionClose(
+            int senderUid, String senderPackageName, int sessionId) {
+        Log.d(TAG, senderPackageName + " UID " + senderUid + " close MusicFx session " + sessionId);
+
+        PackageSessions pkgSessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
+        if (pkgSessions == null) {
+            Log.e(TAG, senderPackageName + " UID " + senderUid + " does not exist in map, abort");
+            return false;
+        }
+        if (pkgSessions.mPackageName != senderPackageName) {
+            Log.w(TAG, "Inconsistency package names for UID " + senderUid + " close, prev: "
+                    + pkgSessions.mPackageName + ", now: " + senderPackageName);
+            return false;
+        }
+
+        if (pkgSessions.mSessions != null && pkgSessions.mSessions.size() != 0) {
+            if (!pkgSessions.mSessions.contains(sessionId)) {
+                Log.e(TAG, senderPackageName + " UID " + senderUid + " session " + sessionId
+                        + " does not exist in map, abort");
+                return false;
+            }
+
+            pkgSessions.mSessions.remove(Integer.valueOf(sessionId));
+        }
+
+        if (pkgSessions.mSessions == null || pkgSessions.mSessions.size() == 0) {
+            // remove UID from map as well as the UID observer with the last session close
+            mClientUidSessionMap.remove(Integer.valueOf(senderUid));
+        } else {
+            mClientUidSessionMap.put(Integer.valueOf(senderUid), pkgSessions);
+        }
+
+        return true;
+    }
+
+    /**
+     * @return true if the intent is validated and handled successfully, false with any error
+     * (invalid sender/intent for example).
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+            android.Manifest.permission.INTERACT_ACROSS_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES
+    })
+    private boolean setMusicFxServiceWithObserver(
+            Intent intent, int senderUid, String packageName) {
+        final int session = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION,
+                AudioManager.AUDIO_SESSION_ID_GENERATE);
+        if (AudioManager.AUDIO_SESSION_ID_GENERATE == session) {
+            Log.e(TAG, packageName + " intent have no invalid audio session");
+            return false;
+        }
+
+        synchronized (mClientUidMapLock) {
+            if (intent.getAction().equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) {
+                return handleAudioEffectSessionOpen(senderUid, packageName, session);
+            } else {
+                return handleAudioEffectSessionClose(senderUid, packageName, session);
+            }
+        }
+    }
+
+    private int getCurrentUserId() {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            UserInfo currentUser = ActivityManager.getService().getCurrentUser();
+            return currentUser.id;
+        } catch (RemoteException e) {
+            // Activity manager not running, nothing we can do assume user 0.
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return UserHandle.USER_SYSTEM;
+    }
+
+
+    /**
+     * Handle the UidObserver onUidGone callback of MusicFx clients.
+     * Send close intent for all open audio sessions of this UID. The mClientUidSessionMap will be
+     * updated with the handling of close intent in setMusicFxServiceWithObserver.
+     */
+    @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS})
+    private void handleEffectClientUidGone(int uid) {
+        synchronized (mClientUidMapLock) {
+            Log.d(TAG, "handle MSG_EFFECT_CLIENT_GONE uid: " + uid + " mapSize: "
+                    + mClientUidSessionMap.size());
+            // Once the uid is no longer running, close all remain audio session(s) for this UID
+            final PackageSessions pkgSessions = mClientUidSessionMap.get(Integer.valueOf(uid));
+            if (pkgSessions != null) {
+                Log.i(TAG, "UID " + uid + " gone, closing all sessions");
+
+                // send close intent for each open session of the gone UID
+                for (Integer sessionId : pkgSessions.mSessions) {
+                    Intent closeIntent =
+                            new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
+                    closeIntent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, pkgSessions.mPackageName);
+                    closeIntent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId);
+                    closeIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+                    // set broadcast target
+                    closeIntent.setPackage(mMusicFxPackageName);
+                    mContext.sendBroadcastAsUser(closeIntent, UserHandle.ALL);
+                }
+                mClientUidSessionMap.remove(Integer.valueOf(uid));
+            }
+        }
+    }
+
+    @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS})
+    /*package*/ void handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_EFFECT_CLIENT_GONE:
+                Log.w(TAG, " handle MSG_EFFECT_CLIENT_GONE");
+                handleEffectClientUidGone(msg.arg1 /* uid */);
+                break;
+            default:
+                Log.e(TAG, "Unexpected msg to handle in MusicFxHelper: " + msg.what);
+                break;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 81365bf..6c5f3e7 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -108,11 +108,11 @@
     private static final int SAFE_MEDIA_VOLUME_INACTIVE = 2;  // confirmed
     private static final int SAFE_MEDIA_VOLUME_ACTIVE = 3;  // unconfirmed
 
-    private static final int MSG_CONFIGURE_SAFE_MEDIA = SAFE_MEDIA_VOLUME_MSG_START + 1;
-    private static final int MSG_CONFIGURE_SAFE_MEDIA_FORCED = SAFE_MEDIA_VOLUME_MSG_START + 2;
-    private static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 3;
-    private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 4;
-    private static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 5;
+    /*package*/ static final int MSG_CONFIGURE_SAFE_MEDIA = SAFE_MEDIA_VOLUME_MSG_START + 1;
+    /*package*/ static final int MSG_CONFIGURE_SAFE_MEDIA_FORCED = SAFE_MEDIA_VOLUME_MSG_START + 2;
+    /*package*/ static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 3;
+    /*package*/ static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 4;
+    /*package*/ static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 5;
     /*package*/ static final int MSG_CSD_UPDATE_ATTENUATION = SAFE_MEDIA_VOLUME_MSG_START + 6;
 
     private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0dc4467..42c2548 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -352,7 +352,6 @@
                 mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
             } else if (phase == PHASE_BOOT_COMPLETED) {
                 mLockSettingsService.loadEscrowData();
-                mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
             }
         }
 
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 3d4f866..d66b9b9 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -182,7 +182,7 @@
 
     auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
     auto hash_block_count = block_count;
-    for (auto i = 0; hash_block_count > 1; i++) {
+    while (hash_block_count > 1) {
         hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
         total_tree_block_count += hash_block_count;
     }
diff --git a/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
new file mode 100644
index 0000000..472a82c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright 2023 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.audio;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.audiofx.AudioEffect;
+import android.os.Message;
+import android.util.Log;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class MusicFxHelperTest {
+    private static final String TAG = "MusicFxHelperTest";
+
+    @Mock private AudioService mMockAudioService;
+    @Mock private Context mMockContext;
+    @Mock private PackageManager mMockPackageManager;
+
+    private ResolveInfo mResolveInfo1 = new ResolveInfo();
+    private ResolveInfo mResolveInfo2 = new ResolveInfo();
+    private final String mTestPkg1 = "testPkg1", mTestPkg2 = "testPkg2", mTestPkg3 = "testPkg3";
+    private final String mMusicFxPkgName = "com.android.musicfx";
+    private final int mTestUid1 = 1, mTestUid2 = 2, mTestUid3 = 3, mMusicFxUid = 78;
+    private final int mTestSession1 = 11, mTestSession2 = 22, mTestSession3 = 33;
+
+    private List<ResolveInfo> mEmptyList = new ArrayList<>();
+    private List<ResolveInfo> mSingleList = new ArrayList<>();
+    private List<ResolveInfo> mDoubleList = new ArrayList<>();
+
+    // the class being unit-tested here
+    @InjectMocks private MusicFxHelper mMusicFxHelper;
+
+    @Before
+    @SuppressWarnings("DirectInvocationOnMock")
+    public void setUp() throws Exception {
+        mMockAudioService = mock(AudioService.class);
+        mMusicFxHelper = mMockAudioService.getMusicFxHelper();
+        MockitoAnnotations.initMocks(this);
+
+        mResolveInfo1.activityInfo = new ActivityInfo();
+        mResolveInfo1.activityInfo.packageName = mTestPkg1;
+        mResolveInfo2.activityInfo = new ActivityInfo();
+        mResolveInfo2.activityInfo.packageName = mTestPkg2;
+
+        mSingleList.add(mResolveInfo1);
+        mDoubleList.add(mResolveInfo1);
+        mDoubleList.add(mResolveInfo2);
+
+        Assert.assertNotNull(mMusicFxHelper);
+    }
+
+    private Intent newIntent(String action, String packageName, int sessionId) {
+        Intent intent = new Intent(action);
+        if (packageName != null) {
+            intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, packageName);
+        }
+        intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId);
+        return intent;
+    }
+
+    /**
+     * Helper function to send ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION intent with verification.
+     *
+     * @throws NameNotFoundException if no such package is available to the caller.
+     */
+    private void openSessionWithResList(
+            List<ResolveInfo> list, int bind, int broadcast, String packageName, int audioSession,
+            int uid) {
+        doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+        doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt());
+        if (list != null && list.size() != 0) {
+            try {
+                doReturn(uid).when(mMockPackageManager)
+                        .getPackageUidAsUser(eq(packageName), anyObject(), anyInt());
+                doReturn(mMusicFxUid).when(mMockPackageManager)
+                        .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt());
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "NameNotFoundException: " + e);
+            }
+        }
+
+        Intent intent = newIntent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION,
+                packageName, audioSession);
+        mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
+        verify(mMockContext, times(bind))
+                .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
+        verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject());
+    }
+
+    /**
+     * Helper function to send ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION intent with verification.
+     *
+     * @throws NameNotFoundException if no such package is available to the caller.
+     */
+    private void closeSessionWithResList(
+                List<ResolveInfo> list, int unBind, int broadcast, String packageName,
+                int audioSession, int uid) {
+        doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+        doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt());
+        if (list != null && list.size() != 0) {
+            try {
+                doReturn(uid).when(mMockPackageManager)
+                        .getPackageUidAsUser(eq(packageName), anyObject(), anyInt());
+                doReturn(mMusicFxUid).when(mMockPackageManager)
+                        .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt());
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "NameNotFoundException: " + e);
+            }
+        }
+
+        Intent intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION,
+                                packageName, audioSession);
+        mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
+        verify(mMockContext, times(unBind)).unbindService(anyObject());
+        verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject());
+    }
+
+    /**
+     * Helper function to send MSG_EFFECT_CLIENT_GONE message with verification.
+     */
+    private void sendMessage(int msgId, int uid, int unBinds, int broadcasts) {
+        mMusicFxHelper.handleMessage(Message.obtain(null, msgId, uid /* arg1 */, 0 /* arg2 */));
+        verify(mMockContext, times(broadcasts)).sendBroadcastAsUser(anyObject(), anyObject());
+        verify(mMockContext, times(unBinds)).unbindService(anyObject());
+    }
+
+    /**
+     * Send invalid message to MusicFxHelper.
+     */
+    @Test
+    public void testInvalidMessage() {
+        Log.i(TAG, "running testInvalidMessage");
+
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE - 1, 0, 0, 0);
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE + 1, 0, 0, 0);
+    }
+
+    /**
+     * Send client gone message to MusicFxHelper when no client exist.
+     */
+    @Test
+    public void testGoneMessageWhenNoClient() {
+        Log.i(TAG, "running testGoneMessageWhenNoClient");
+
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, 0, 0, 0);
+    }
+
+    /**
+     * Send ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION intent to MusicFxHelper when no session exist.
+     */
+    @Test
+    public void testCloseBroadcastIntent() {
+        Log.i(TAG, "running testCloseBroadcastIntent");
+
+        closeSessionWithResList(null, 0, 0, null, mTestSession1, mTestUid1);
+    }
+
+    /**
+     * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION intent when target application package was set.
+     * When the target application package was set for an intent, it means this intent is limited
+     * to a specific target application, as a result MusicFxHelper will not handle this intent.
+     */
+    @Test
+    public void testBroadcastIntentWithPackage() {
+        Log.i(TAG, "running testBroadcastIntentWithPackage");
+
+        Intent intent = newIntent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION, null, 1);
+        intent.setPackage(mTestPkg1);
+        mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
+        verify(mMockContext, times(0))
+                .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
+        verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject());
+
+        intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION, null, 1);
+        intent.setPackage(mTestPkg2);
+        mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
+        verify(mMockContext, times(0))
+                .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
+        verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject());
+    }
+
+    /**
+     * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION with no broadcast receiver.
+     */
+    @Test
+    public void testBroadcastIntentWithNoPackageAndNoBroadcastReceiver() {
+        Log.i(TAG, "running testBroadcastIntentWithNoPackageAndNoBroadcastReceiver");
+
+        openSessionWithResList(mEmptyList, 0, 0, null, mTestSession1, mTestUid1);
+        closeSessionWithResList(mEmptyList, 0, 0, null, mTestSession1, mTestUid1);
+    }
+
+    /**
+     * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION with one broadcast receiver.
+     */
+    @Test
+    public void testBroadcastIntentWithNoPackageAndOneBroadcastReceiver() {
+        Log.i(TAG, "running testBroadcastIntentWithNoPackageAndOneBroadcastReceiver");
+
+        int broadcasts = 1, bind = 1, unbind = 1;
+        openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession1, mTestUid1);
+
+        // repeat with different session ID
+        broadcasts = broadcasts + 1;
+        bind = bind + 1;
+        unbind = unbind + 1;
+        openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession2, mTestUid1);
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession2, mTestUid1);
+
+        // repeat with different UID
+        broadcasts = broadcasts + 1;
+        bind = bind + 1;
+        unbind = unbind + 1;
+        openSessionWithResList(mSingleList, bind, broadcasts, null, mTestSession1, mTestUid2);
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mSingleList, unbind, broadcasts, null, mTestSession1, mTestUid2);
+    }
+
+    /**
+     * OPEN/CLOSE AUDIO_EFFECT_CONTROL_SESSION with two broadcast receivers.
+     */
+    @Test
+    public void testBroadcastIntentWithNoPackageAndTwoBroadcastReceivers() {
+        Log.i(TAG, "running testBroadcastIntentWithNoPackageAndTwoBroadcastReceivers");
+
+        openSessionWithResList(mDoubleList, 1, 1, null, mTestSession1, mTestUid1);
+        closeSessionWithResList(mDoubleList, 1, 2, null, mTestSession1, mTestUid1);
+    }
+
+    /**
+     * Open/close session UID not matching.
+     * No broadcast for mismatching sessionID/UID/packageName.
+     */
+    @Test
+    public void testBroadcastBadIntents() {
+        Log.i(TAG, "running testBroadcastBadIntents");
+
+        int broadcasts = 1;
+        openSessionWithResList(mSingleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        // mismatch UID
+        closeSessionWithResList(mSingleList, 0, broadcasts, mTestPkg1, mTestSession1, mTestUid2);
+        // mismatch AudioSession
+        closeSessionWithResList(mSingleList, 0, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+        // mismatch packageName
+        closeSessionWithResList(mSingleList, 0, broadcasts, mTestPkg2, mTestSession1, mTestUid1);
+
+        // cleanup with correct UID and session ID
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mSingleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+    }
+
+    /**
+     * Open/close sessions with one UID, some with correct intents some with illegal intents.
+     * No broadcast for mismatching sessionID/UID/packageName.
+     */
+    @Test
+    public void testBroadcastGoodAndBadIntents() {
+        Log.i(TAG, "running testBroadcastGoodAndBadIntents");
+
+        int broadcasts = 1, bind = 1, unbind = 0;
+        openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        // mismatch packageName, session ID and UID
+        closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+                                mTestUid2);
+        // mismatch session ID and mismatch UID
+        closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+                                mTestUid2);
+        // mismatch packageName and mismatch UID
+        closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession1,
+                                mTestUid2);
+        // mismatch packageName and sessionID
+        closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+                                mTestUid1);
+        // inconsistency package name for same UID
+        openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid1);
+        // open session2 with good intent
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mSingleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+
+        // cleanup with correct UID and session ID
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+                                mTestUid1);
+        broadcasts = broadcasts + 1;
+        unbind = unbind + 1;
+        closeSessionWithResList(mSingleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+                                mTestUid1);
+    }
+
+    /**
+     * Send ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION when there is no listener.
+     */
+    @Test
+    public void testBroadcastOpenSessionWithValidPackageNameAndNoListener() {
+        Log.i(TAG, "running testBroadcastOpenSessionWithValidPackageNameAndNoListener");
+
+        // null listener list should not trigger any action
+        openSessionWithResList(null, 0, 0, mTestPkg1, mTestSession1, mTestUid1);
+        // empty listener list should not trigger any action
+        openSessionWithResList(mEmptyList, 0, 0, mTestPkg1, mTestSession1, mTestUid1);
+    }
+
+    /**
+     * One MusicFx client, open session and close.
+     */
+    @Test
+    public void testOpenCloseAudioSession() {
+        Log.i(TAG, "running testOpenCloseAudioSession");
+
+        int broadcasts = 1;
+        openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+    }
+
+    /**
+     * One MusicFx client, open session and close, then gone.
+     */
+    @Test
+    public void testOpenCloseAudioSessionAndGone() {
+        Log.i(TAG, "running testOpenCloseAudioSessionAndGone");
+
+        int broadcasts = 1;
+        openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, 0, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+
+        broadcasts = broadcasts + 1; // 1 open session left
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, 1, broadcasts);
+    }
+
+    /**
+     * One MusicFx client, open session, then UID gone without close.
+     */
+    @Test
+    public void testOpenOneSessionAndGo() {
+        Log.i(TAG, "running testOpenOneSessionAndGo");
+
+        int broadcasts = 1;
+        openSessionWithResList(mDoubleList, 1, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+
+        broadcasts = broadcasts + 1;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, 1, broadcasts);
+    }
+
+    /**
+     * Two MusicFx clients open and close sessions.
+     */
+    @Test
+    public void testOpenTwoSessionsAndClose() {
+        Log.i(TAG, "running testOpenTwoSessionsAndClose");
+
+        int broadcasts = 1, bind = 1, unbind = 0;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+                mTestUid2);
+        broadcasts = broadcasts + 1;
+        unbind = unbind + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+                mTestUid1);
+
+        broadcasts = broadcasts + 1;
+        bind = bind + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+                mTestUid1);
+        broadcasts = broadcasts + 1;
+        unbind = unbind + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+                mTestUid2);
+    }
+
+    /**
+     * Two MusicFx clients open sessions, then both UID gone without close.
+     */
+    @Test
+    public void testOpenTwoSessionsAndGo() {
+        Log.i(TAG, "running testOpenTwoSessionsAndGo");
+
+        int broadcasts = 1, bind = 1, unbind = 0;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+
+        broadcasts = broadcasts + 1;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, unbind, broadcasts);
+
+        broadcasts = broadcasts + 1;
+        unbind = unbind + 1;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+    }
+
+    /**
+     * Two MusicFx clients open sessions, one close but not gone, the other one gone without close.
+     */
+    @Test
+    public void testTwoSessionsOpenOneCloseOneGo() {
+        Log.i(TAG, "running testTwoSessionsOpneAndOneCloseOneGo");
+
+        int broadcasts = 1, bind = 1, unbind = 0;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+                                mTestUid1);
+
+        broadcasts = broadcasts + 1;
+        unbind = unbind + 1;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+    }
+
+    /**
+     * One MusicFx client, open multiple audio sessions, and close all sessions.
+     */
+    @Test
+    public void testTwoSessionsInSameUidOpenClose() {
+        Log.i(TAG, "running testTwoSessionsOpneAndOneCloseOneGo");
+
+        int broadcasts = 1, bind = 1, unbind = 0;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+                                mTestUid1);
+        broadcasts = broadcasts + 1;
+        unbind = unbind + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+                                mTestUid1);
+    }
+
+    /**
+     * Three MusicFx clients, each with multiple audio sessions, and close all sessions.
+     */
+    @Test
+    public void testThreeSessionsInThreeUidOpenClose() {
+        Log.i(TAG, "running testThreeSessionsInThreeUidOpenClose");
+
+        int broadcasts = 1, bind = 1, unbind = 0;
+        //client1
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+        // client2
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession3, mTestUid2);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+        // client3
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid3);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession3, mTestUid3);
+
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+                                mTestUid1);
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg3, mTestSession3,
+                                mTestUid3);
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession2,
+                                mTestUid2);
+        // all sessions of client1 closed
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+                                mTestUid1);
+        // all sessions of client3 closed
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg3, mTestSession1,
+                                mTestUid3);
+        // all sessions of client2 closed
+        broadcasts = broadcasts + 1;
+        // now expect unbind to happen
+        unbind = unbind + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg2, mTestSession3,
+                                mTestUid2);
+    }
+
+    /**
+     * Two MusicFx clients, with multiple audio sessions, one close all sessions, and other gone.
+     */
+    @Test
+    public void testTwoUidOneCloseOneGo() {
+        Log.i(TAG, "running testTwoUidOneCloseOneGo");
+
+        int broadcasts = 1, bind = 1, unbind = 0;
+        //client1
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+        // client2
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession1, mTestUid2);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+                                mTestUid1);
+        // client2 gone
+        broadcasts = broadcasts + 2;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+        // client 1 close all sessions
+        broadcasts = broadcasts + 1;
+        unbind = unbind + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+                                mTestUid1);
+    }
+
+    /**
+     * Three MusicFx clients, with multiple audio sessions, all UID gone.
+     */
+    @Test
+    public void testThreeUidAllGo() {
+        Log.i(TAG, "running testThreeUidAllGo");
+
+        int broadcasts = 1, bind = 1, unbind = 0;
+        //client1
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+        // client2
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession3, mTestUid2);
+        // client3
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession3, mTestUid3);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid3);
+
+        // client2 gone
+        broadcasts = broadcasts + 2;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+        // client3 gone
+        broadcasts = broadcasts + 2;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid3, unbind, broadcasts);
+        // client 1 gone
+        broadcasts = broadcasts + 2;
+        // now expect unbindService to happen
+        unbind = unbind + 1;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid1, unbind, broadcasts);
+    }
+
+    /**
+     * Three MusicFx clients, multiple audio sessions, open and UID gone in difference sequence.
+     */
+    @Test
+    public void testThreeUidDiffSequence() {
+        Log.i(TAG, "running testThreeUidDiffSequence");
+
+        int broadcasts = 1, bind = 1, unbind = 0;
+        //client1
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession1, mTestUid1);
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg1, mTestSession2, mTestUid1);
+        // client2
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession2, mTestUid2);
+        // client1 close one session
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession1,
+                                mTestUid1);
+        // client2 open another session
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg2, mTestSession3, mTestUid2);
+        // client3 open one session
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession3, mTestUid3);
+        // client2 gone
+        broadcasts = broadcasts + 2;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid2, unbind, broadcasts);
+        // client3 open another session
+        broadcasts = broadcasts + 1;
+        openSessionWithResList(mDoubleList, bind, broadcasts, mTestPkg3, mTestSession1, mTestUid3);
+        // client1 close another session, and gone
+        broadcasts = broadcasts + 1;
+        closeSessionWithResList(mDoubleList, unbind, broadcasts, mTestPkg1, mTestSession2,
+                                mTestUid1);
+        // last UID client3 gone, unbind
+        broadcasts = broadcasts + 2;
+        unbind = unbind + 1;
+        sendMessage(MusicFxHelper.MSG_EFFECT_CLIENT_GONE, mTestUid3, unbind, broadcasts);
+    }
+}
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
index bfd3508..c901efa 100644
--- a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
+++ b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
@@ -26,4 +26,5 @@
 android_test {
     name: "AaptAutoVersionTest",
     sdk_version: "current",
+    use_resource_processor: false,
 }
diff --git a/tools/aapt2/integration-tests/BasicTest/Android.bp b/tools/aapt2/integration-tests/BasicTest/Android.bp
index 7db9d26..d0649ea 100644
--- a/tools/aapt2/integration-tests/BasicTest/Android.bp
+++ b/tools/aapt2/integration-tests/BasicTest/Android.bp
@@ -26,4 +26,5 @@
 android_test {
     name: "AaptBasicTest",
     sdk_version: "current",
+    use_resource_processor: false,
 }
diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
index 80404ee..ebb4e9f 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
@@ -24,9 +24,9 @@
 }
 
 android_test {
-
     name: "AaptTestStaticLib_App",
     sdk_version: "current",
+    use_resource_processor: false,
     srcs: ["src/**/*.java"],
     asset_dirs: [
         "assets",
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
index a84da43..ee12a929 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
@@ -26,6 +26,7 @@
 android_library {
     name: "AaptTestStaticLib_LibOne",
     sdk_version: "current",
+    use_resource_processor: false,
     srcs: ["src/**/*.java"],
     resource_dirs: ["res"],
 }
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
index d386c3a..83b23624 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
@@ -26,6 +26,7 @@
 android_library {
     name: "AaptTestStaticLib_LibTwo",
     sdk_version: "current",
+    use_resource_processor: false,
     srcs: ["src/**/*.java"],
     resource_dirs: ["res"],
     libs: ["AaptTestStaticLib_LibOne"],
diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
index 1e8cf86..6fcdf1c 100644
--- a/tools/aapt2/integration-tests/SymlinkTest/Android.bp
+++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
@@ -26,4 +26,8 @@
 android_test {
     name: "AaptSymlinkTest",
     sdk_version: "current",
+    use_resource_processor: false,
+    compile_data: [
+        "targets/*",
+    ],
 }