+ private class RmtSbmxFullVolDeathHandler implements IBinder.DeathRecipient {
+ private IBinder mICallback; // To be notified of client's death
+
+ RmtSbmxFullVolDeathHandler(IBinder cb) {
+ mICallback = cb;
+ try {
+ cb.linkToDeath(this, 0/*flags*/);
+ } catch (RemoteException e) {
+ Log.e(TAG, "can't link to death", e);
+ }
+ }
+
+ boolean isHandlerFor(IBinder cb) {
+ return mICallback.equals(cb);
+ }
+
+ void forget() {
+ try {
+ mICallback.unlinkToDeath(this, 0/*flags*/);
+ } catch (NoSuchElementException e) {
+ Log.e(TAG, "error unlinking to death", e);
+ }
+ }
+
+ public void binderDied() {
+ Log.w(TAG, "Recorder with remote submix at full volume died " + mICallback);
+ forceRemoteSubmixFullVolume(false, mICallback);
+ }
+ }
+
+ /**
+ * call must be synchronized on mRmtSbmxFullVolDeathHandlers
+ * @return true if there is a registered death handler, false otherwise */
+ private boolean discardRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
+ Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
+ while (it.hasNext()) {
+ final RmtSbmxFullVolDeathHandler handler = it.next();
+ if (handler.isHandlerFor(cb)) {
+ handler.forget();
+ mRmtSbmxFullVolDeathHandlers.remove(handler);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** call synchronized on mRmtSbmxFullVolDeathHandlers */
+ private boolean hasRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
+ Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
+ while (it.hasNext()) {
+ if (it.next().isHandlerFor(cb)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private int mRmtSbmxFullVolRefCount = 0;
+ private ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers =
+ new ArrayList<RmtSbmxFullVolDeathHandler>();
+
+ public void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb) {
+ if (cb == null) {
+ return;
+ }
+ if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.CAPTURE_AUDIO_OUTPUT))) {
+ Log.w(TAG, "Trying to call forceRemoteSubmixFullVolume() without CAPTURE_AUDIO_OUTPUT");
+ return;
+ }
+ synchronized(mRmtSbmxFullVolDeathHandlers) {
+ boolean applyRequired = false;
+ if (startForcing) {
+ if (!hasRmtSbmxFullVolDeathHandlerFor(cb)) {
+ mRmtSbmxFullVolDeathHandlers.add(new RmtSbmxFullVolDeathHandler(cb));
+ if (mRmtSbmxFullVolRefCount == 0) {
+ mFullVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ applyRequired = true;
+ }
+ mRmtSbmxFullVolRefCount++;
+ }
+ } else {
+ if (discardRmtSbmxFullVolDeathHandlerFor(cb) && (mRmtSbmxFullVolRefCount > 0)) {
+ mRmtSbmxFullVolRefCount--;
+ if (mRmtSbmxFullVolRefCount == 0) {
+ mFullVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ mFixedVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ applyRequired = true;
+ }
+ }
+ }
+ if (applyRequired) {
+ // Assumes only STREAM_MUSIC going through DEVICE_OUT_REMOTE_SUBMIX
+ checkAllFixedVolumeDevices(AudioSystem.STREAM_MUSIC);
+ mStreamStates[AudioSystem.STREAM_MUSIC].applyAllVolumes();
+ }
+ }
+ }
+