Merge "FM: Close the service and end the process on user switch"
diff --git a/fmapp2/src/com/caf/fmradio/FMRadio.java b/fmapp2/src/com/caf/fmradio/FMRadio.java
index 1da839b..d6cade4 100644
--- a/fmapp2/src/com/caf/fmradio/FMRadio.java
+++ b/fmapp2/src/com/caf/fmradio/FMRadio.java
@@ -3070,7 +3070,7 @@
    private ServiceConnection osc = new ServiceConnection() {
       public void onServiceConnected(ComponentName classname, IBinder obj) {
          mService = IFMRadioService.Stub.asInterface(obj);
-         Log.e(LOGTAG, "ServiceConnection: onServiceConnected: ");
+         Log.e(LOGTAG, "ServiceConnection: onServiceConnected");
          if (mService != null) {
             try {
                mService.registerCallbacks(mServiceCallbacks);
diff --git a/fmapp2/src/com/caf/fmradio/FMRadioService.java b/fmapp2/src/com/caf/fmradio/FMRadioService.java
index 4c0ef18..f605749 100644
--- a/fmapp2/src/com/caf/fmradio/FMRadioService.java
+++ b/fmapp2/src/com/caf/fmradio/FMRadioService.java
@@ -99,6 +99,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.os.IBinder.DeathRecipient;
 
 
 /**
@@ -152,6 +153,11 @@
    private boolean misAnalogPathEnabled = false;
    private boolean mA2dpDisconnected = false;
    private boolean mA2dpConnected = false;
+
+   //Install the death receipient
+   private IBinder.DeathRecipient mDeathRecipient;
+   private FMDeathRecipient mFMdr;
+
    //PhoneStateListener instances corresponding to each
 
    private FmRxRdsData mFMRxRDSData=null;
@@ -223,7 +229,9 @@
    private static final int FM_OFF_FROM_APPLICATION = 1;
    private static final int FM_OFF_FROM_ANTENNA = 2;
    private static final int RADIO_TIMEOUT = 1500;
-   private static final int RESET_SLIMBUS_DATA_PORT = 1;
+   private static final int DISABLE_SLIMBUS_DATA_PORT = 0;
+   private static final int ENABLE_SLIMBUS_DATA_PORT = 1;
+   private static final int ENABLE_SLIMBUS_CLOCK_DATA = 2;
 
    private static Object mNotchFilterLock = new Object();
 
@@ -237,7 +245,7 @@
    public void onCreate() {
       super.onCreate();
 
-      mFmA2dpDisabled = SystemProperties.getBoolean("fm.a2dp.conc.disabled",false);
+      mFmA2dpDisabled = SystemProperties.getBoolean("vendor.fm.a2dp.conc.disabled",false);
       mPrefs = new FmSharedPreferences(this);
       mCallbacks = null;
       TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
@@ -913,12 +921,21 @@
       mServiceInUse = true;
       /* Application/UI is attached, so get out of lower power mode */
       setLowPowerMode(false);
-      Log.d(LOGTAG, "onBind");
+      Log.d(LOGTAG, "onBind ");
+      //todo check for the mBinder validity
+      mFMdr = new FMDeathRecipient(this, mBinder);
+      try {
+              mBinder.linkToDeath(mFMdr, 0);
+              Log.d(LOGTAG, "onBind mBinder linked to death" + mBinder);
+          } catch (RemoteException e) {
+              Log.e(LOGTAG,"LinktoDeath Exception: "+e + "FM DR =" + mFMdr);
+          }
       return mBinder;
    }
 
    @Override
    public void onRebind(Intent intent) {
+      Log.d(LOGTAG, "onRebind");
       mDelayedStopHandler.removeCallbacksAndMessages(null);
       cancelAlarmDealyedServiceStop();
       mServiceInUse = true;
@@ -926,8 +943,9 @@
       if (isFmOn()) {
           setLowPowerMode(false);
           startFM();
+          if (mReceiver.isCherokeeChip())
+              mReceiver.EnableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
       }
-      Log.d(LOGTAG, "onRebind");
    }
 
    @Override
@@ -943,14 +961,20 @@
 
    @Override
    public boolean onUnbind(Intent intent) {
-      mServiceInUse = false;
-      Log.d(LOGTAG, "onUnbind");
-
-      /* Application/UI is not attached, so go into lower power mode */
-      unregisterCallbacks();
-      setLowPowerMode(true);
-      return true;
-   }
+       mServiceInUse = false;
+       Log.d(LOGTAG, "onUnbind");
+       /* Application/UI is not attached, so go into lower power mode */
+       unregisterCallbacks();
+       setLowPowerMode(true);
+       try{
+           Log.d(LOGTAG,"Unlinking FM Death receipient");
+           mBinder.unlinkToDeath(mFMdr,0);
+       }
+       catch(NoSuchElementException e){
+           Log.e(LOGTAG,"No death recipient registered"+e);
+       }
+       return true;
+  }
 
    private String getProcessName() {
       int id = Process.myPid();
@@ -1591,7 +1615,7 @@
 
    private Runnable mSpeakerDisableTask = new Runnable() {
       public void run() {
-         Log.v(LOGTAG, "Disabling Speaker");
+         Log.v(LOGTAG, "*** Disabling Speaker");
          AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE);
       }
    };
@@ -1637,23 +1661,33 @@
                           stopRecording();
                   case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                       Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
+                      if (mSpeakerPhoneOn) {
+                          Log.v(LOGTAG, "Focus Loss/TLoss - Disabling speaker");
+                          AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE);
+                      }
                       if (mReceiver != null)
-                          mReceiver.EnableSlimbus(RESET_SLIMBUS_DATA_PORT);
+                          mReceiver.EnableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
                       if (true == mPlaybackInProgress) {
                           stopFM();
                       }
+                      mReceiver.EnableSlimbus(DISABLE_SLIMBUS_DATA_PORT);
                       mStoppedOnFocusLoss = true;
                       break;
                   case AudioManager.AUDIOFOCUS_LOSS:
-                      Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS");
+                      Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS mspeakerphone= " +
+                               mSpeakerPhoneOn);
                       //intentional fall through.
                       if (mSpeakerPhoneOn) {
-                         mSpeakerDisableHandler.removeCallbacks(mSpeakerDisableTask);
-                         mSpeakerDisableHandler.postDelayed(mSpeakerDisableTask, 0);
+                          mSpeakerDisableHandler.removeCallbacks(mSpeakerDisableTask);
+                          mSpeakerDisableHandler.postDelayed(mSpeakerDisableTask, 0);
                       }
                       if (true == mPlaybackInProgress) {
                           stopFM();
                       }
+
+                      //intentional fall through.
+                      mReceiver.EnableSlimbus(DISABLE_SLIMBUS_DATA_PORT);
+
                       if (true == isFmRecordingOn())
                           stopRecording();
 
@@ -1665,15 +1699,19 @@
                       mSession.setActive(false);
                       break;
                   case AudioManager.AUDIOFOCUS_GAIN:
-                      Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN");
+                      Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN mPlaybackinprogress =" +
+                           mPlaybackInProgress);
                       mStoppedOnFocusLoss = false;
                       if (mResumeAfterCall) {
                           Log.v(LOGTAG, "resumeAfterCall");
                           resumeAfterCall();
                           break;
                       }
+
                       if(false == mPlaybackInProgress)
                           startFM();
+
+                      mReceiver.EnableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
                       mSession.setActive(true);
                       break;
                   default:
@@ -1754,7 +1792,7 @@
       NotificationChannel notificationChannel =
               new NotificationChannel(FMRADIO_NOTIFICATION_CHANNEL,
               context.getString(R.string.app_name),
-              NotificationManager.IMPORTANCE_HIGH);
+              NotificationManager.IMPORTANCE_LOW);
 
       notificationManager.createNotificationChannel(notificationChannel);
 
@@ -1766,8 +1804,7 @@
             .setOngoing(true)
             .build();
 
-      notificationManager.notify(FMRADIOSERVICE_STATUS, notification);
-
+      startForeground(FMRADIOSERVICE_STATUS, notification);
       mFMOn = true;
    }
 
@@ -2215,60 +2252,27 @@
        return status;
    }
 
-  /*
-   * Turn ON FM: Powers up FM hardware, and initializes the FM module
-   *                                                                                 .
-   * @return true if fm Enable api was invoked successfully, false if the api failed.
-   */
-   private boolean fmOn() {
-      boolean bStatus=false;
-      mWakeLock.acquire(10*1000);
-      if ( TelephonyManager.CALL_STATE_IDLE != getCallState() ) {
-         return bStatus;
-      }
+   private boolean fmTurnOnSequence () {
+       boolean bStatus = false;
+       // This sets up the FM radio device
+       FmConfig config = FmSharedPreferences.getFMConfiguration();
 
-      if(mReceiver == null)
-      {
-         try {
-            mReceiver = new FmReceiver(FMRADIO_DEVICE_FD_STRING, fmCallbacks);
-         }
-         catch (InstantiationException e)
-         {
-            throw new RuntimeException("FmReceiver service not available!");
-         }
-      }
+       Log.d(LOGTAG, "fmOn: RadioBand   :"+ config.getRadioBand());
+       Log.d(LOGTAG, "fmOn: Emphasis    :"+ config.getEmphasis());
+       Log.d(LOGTAG, "fmOn: ChSpacing   :"+ config.getChSpacing());
+       Log.d(LOGTAG, "fmOn: RdsStd      :"+ config.getRdsStd());
+       Log.d(LOGTAG, "fmOn: LowerLimit  :"+ config.getLowerLimit());
+       Log.d(LOGTAG, "fmOn: UpperLimit  :"+ config.getUpperLimit());
 
-      if (mReceiver != null)
-      {
-         if (isFmOn())
-         {
-            /* FM Is already on,*/
-            bStatus = true;
-            Log.d(LOGTAG, "mReceiver.already enabled");
-         }
-         else
-         {
-            // This sets up the FM radio device
-            FmConfig config = FmSharedPreferences.getFMConfiguration();
-            Log.d(LOGTAG, "fmOn: RadioBand   :"+ config.getRadioBand());
-            Log.d(LOGTAG, "fmOn: Emphasis    :"+ config.getEmphasis());
-            Log.d(LOGTAG, "fmOn: ChSpacing   :"+ config.getChSpacing());
-            Log.d(LOGTAG, "fmOn: RdsStd      :"+ config.getRdsStd());
-            Log.d(LOGTAG, "fmOn: LowerLimit  :"+ config.getLowerLimit());
-            Log.d(LOGTAG, "fmOn: UpperLimit  :"+ config.getUpperLimit());
-            mEventReceived = false;
-            bStatus = mReceiver.enable(FmSharedPreferences.getFMConfiguration(), this);
+       mEventReceived = false;
+       bStatus = mReceiver.enable(FmSharedPreferences.getFMConfiguration(), this);
 
-            if (mReceiver.isCherokeeChip()) {
-                bStatus = waitForEvent();
-            }
-            if (isSpeakerEnabled()) {
-                setAudioPath(false);
-            } else {
-                setAudioPath(true);
-            }
-            Log.d(LOGTAG, "mReceiver.enable done, Status :" +  bStatus);
-         }
+       if (isSpeakerEnabled()) {
+           setAudioPath(false);
+       } else {
+           setAudioPath(true);
+       }
+       Log.d(LOGTAG, "mReceiver.enable done, Status :" +  bStatus);
 
          if (bStatus == true)
          {
@@ -2323,8 +2327,141 @@
             stop();
          }
 
-         /* reset SSR flag */
-         mIsSSRInProgressFromActivity = false;
+         return bStatus;
+   }
+
+  /*
+   * Turn ON FM: Powers up FM hardware, and initializes the FM module
+   *                                                                                 .
+   * @return true if fm Enable api was invoked successfully, false if the api failed.
+   */
+   private boolean fmTurnOnSequenceCherokee () {
+       boolean bStatus = false;
+
+       // Send command to power up SB slave block
+       mEventReceived = false;
+       mReceiver.EnableSlimbus(ENABLE_SLIMBUS_CLOCK_DATA);
+       bStatus = waitForEvent();
+
+       // Send command to enable FM core
+       mEventReceived = false;
+       mReceiver.EnableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+       bStatus = waitForEvent();
+
+       AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+       if ((audioManager != null) & (false == mPlaybackInProgress)) {
+           Log.d(LOGTAG, "mAudioManager.setFmRadioOn = true \n" );
+           int state =  getCallState();
+           if (TelephonyManager.CALL_STATE_IDLE != getCallState())
+           {
+               fmActionOnCallState(state);
+           } else {
+               startFM(); // enable FM Audio only when Call is IDLE
+           }
+           Log.d(LOGTAG, "mAudioManager.setFmRadioOn done \n" );
+       }
+
+       // This sets up the FM radio device
+       FmConfig config = FmSharedPreferences.getFMConfiguration();
+       Log.d(LOGTAG, "fmOn: RadioBand   :"+ config.getRadioBand());
+       Log.d(LOGTAG, "fmOn: Emphasis    :"+ config.getEmphasis());
+       Log.d(LOGTAG, "fmOn: ChSpacing   :"+ config.getChSpacing());
+       Log.d(LOGTAG, "fmOn: RdsStd      :"+ config.getRdsStd());
+       Log.d(LOGTAG, "fmOn: LowerLimit  :"+ config.getLowerLimit());
+       Log.d(LOGTAG, "fmOn: UpperLimit  :"+ config.getUpperLimit());
+
+       // Enable FM receiver
+       mEventReceived = false;
+       bStatus = mReceiver.enable(FmSharedPreferences.getFMConfiguration(), this);
+       bStatus = waitForEvent();
+
+       if (isSpeakerEnabled()) {
+           setAudioPath(false);
+       } else {
+           setAudioPath(true);
+       }
+       Log.d(LOGTAG, "mReceiver.enable done, Status :" +  bStatus);
+
+       if (bStatus == true) {
+           /* Put the hardware into normal mode */
+           bStatus = setLowPowerMode(false);
+           Log.d(LOGTAG, "setLowPowerMode done, Status :" +  bStatus);
+
+           if (mReceiver != null) {
+               bStatus = mReceiver.registerRdsGroupProcessing(FmReceiver.FM_RX_RDS_GRP_RT_EBL|
+                                                           FmReceiver.FM_RX_RDS_GRP_PS_EBL|
+                                                           FmReceiver.FM_RX_RDS_GRP_AF_EBL|
+                                                           FmReceiver.FM_RX_RDS_GRP_PS_SIMPLE_EBL|
+                                                           FmReceiver.FM_RX_RDS_GRP_ECC_EBL|
+                                                           FmReceiver.FM_RX_RDS_GRP_PTYN_EBL|
+                                                           FmReceiver.FM_RX_RDS_GRP_RT_PLUS_EBL);
+                Log.d(LOGTAG, "registerRdsGroupProcessing done, Status :" +  bStatus);
+            }
+            bStatus = enableAutoAF(FmSharedPreferences.getAutoAFSwitch());
+            Log.d(LOGTAG, "enableAutoAF done, Status :" +  bStatus);
+
+            /* There is no internal Antenna*/
+            bStatus = mReceiver.setInternalAntenna(false);
+            Log.d(LOGTAG, "setInternalAntenna done, Status :" +  bStatus);
+
+            /* Read back to verify the internal Antenna mode*/
+            readInternalAntennaAvailable();
+
+            startNotification();
+            bStatus = true;
+       } else {
+            mReceiver = null; // as enable failed no need to disable
+                              // failure of enable can be because handle
+                              // already open which gets effected if
+                              // we disable
+            stop();
+       }
+
+       return bStatus;
+   }
+
+  /*
+   * Turn ON FM: Powers up FM hardware, and initializes the FM module
+   *                                                                                 .
+   * @return true if fm Enable api was invoked successfully, false if the api failed.
+   */
+   private boolean fmOn () {
+      boolean bStatus = false;
+      mWakeLock.acquire(10*1000);
+
+      if (TelephonyManager.CALL_STATE_IDLE != getCallState()) {
+         return bStatus;
+      }
+
+      if (mReceiver == null)
+      {
+         try {
+            mReceiver = new FmReceiver(FMRADIO_DEVICE_FD_STRING, fmCallbacks);
+         }
+         catch (InstantiationException e)
+         {
+            throw new RuntimeException("FmReceiver service not available!");
+         }
+      }
+
+      if (mReceiver != null)
+      {
+         if (isFmOn())
+         {
+            /* FM Is already on,*/
+            bStatus = true;
+            Log.d(LOGTAG, "mReceiver.already enabled");
+         }
+         else
+         {
+           if (mReceiver.isCherokeeChip()) {
+               bStatus = fmTurnOnSequenceCherokee();
+           } else {
+               bStatus = fmTurnOnSequence();
+           }
+           /* reset SSR flag */
+           mIsSSRInProgressFromActivity = false;
+         }
       }
       return(bStatus);
    }
@@ -3197,7 +3334,8 @@
    /* Receiver callbacks back from the FM Stack */
    FmRxEvCallbacksAdaptor fmCallbacks = new FmRxEvCallbacksAdaptor()
    {
-      public void FmRxEvEnableReceiver() {
+      public void FmRxEvEnableReceiver()
+      {
          Log.d(LOGTAG, "FmRxEvEnableReceiver");
          mReceiver.setRawRdsGrpMask();
          if (mReceiver != null && mReceiver.isCherokeeChip()) {
@@ -3652,6 +3790,16 @@
       {
          Log.d(LOGTAG, "FmRxEvRdsPiMatchRegDone");
       }
+      public void FmRxEvEnableSlimbus(int status)
+      {
+         Log.e(LOGTAG, "FmRxEvEnableSlimbus status = " + status);
+         if (mReceiver != null && mReceiver.isCherokeeChip()) {
+             synchronized(mEventWaitLock) {
+                 mEventReceived = true;
+                 mEventWaitLock.notify();
+             }
+         }
+      }
    };
 
 
@@ -3912,7 +4060,8 @@
        else
            cancelAlarmRecordTimeout();
    }
-   private void requestFocus() {
+
+   private void requestFocusImpl() {
       if( (false == mPlaybackInProgress) &&
           (true  == mStoppedOnFocusLoss) && isFmOn()) {
            // adding code for audio focus gain.
@@ -3923,6 +4072,33 @@
            mStoppedOnFocusLoss = false;
        }
    }
+
+   private void requestFocusImplCherokee() {
+      Log.d(LOGTAG, "++requestFocusImplCherokee mPlaybackInProgress: " +
+                    mPlaybackInProgress + " mStoppedOnFocusLoss: " +
+                    mStoppedOnFocusLoss + " isFmOn: " + isFmOn());
+      if( (false == mPlaybackInProgress) &&
+          (true  == mStoppedOnFocusLoss) && isFmOn()) {
+           // adding code for audio focus gain.
+           AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+           audioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
+                  AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+           startFM();
+           mReceiver.EnableSlimbus(ENABLE_SLIMBUS_DATA_PORT);
+           mStoppedOnFocusLoss = false;
+       }
+   }
+
+   private void requestFocus() {
+       Log.d(LOGTAG, "++requestFocus");
+       if (mReceiver.isCherokeeChip()) {
+           requestFocusImplCherokee();
+       } else {
+           requestFocusImpl();
+       }
+       Log.d(LOGTAG, "--requestFocus");
+   }
+
    private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
        public void onAudioFocusChange(int focusChange) {
            mDelayedStopHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget();
@@ -3964,4 +4140,14 @@
    private void restoreDefaults () {
         mStoppedOnFactoryReset = true;
    }
+
+
+   class FMDeathRecipient implements IBinder.DeathRecipient {
+       public FMDeathRecipient(FMRadioService service, IBinder binder) {
+       }
+       public void binderDied() {
+           Log.d(LOGTAG, "** Binder is dead - cleanup audio now ** ");
+           //TODO unregister the fm service here.
+       }
+   }
 }
diff --git a/fmapp2/src/com/caf/fmradio/FMTransmitterService.java b/fmapp2/src/com/caf/fmradio/FMTransmitterService.java
index e3cf3d6..1d964c4 100644
--- a/fmapp2/src/com/caf/fmradio/FMTransmitterService.java
+++ b/fmapp2/src/com/caf/fmradio/FMTransmitterService.java
@@ -1018,6 +1018,11 @@
             e.printStackTrace();
          }
       }
+
+      public void FmRxEvEnableSlimbus(int status)
+      {
+         Log.d(LOGTAG, "FmRxEvEnableReceiver status = " + status);
+      }
    };
 
 
diff --git a/helium/radio-helium.h b/helium/radio-helium.h
index 200e006..79f61bd 100644
--- a/helium/radio-helium.h
+++ b/helium/radio-helium.h
@@ -171,6 +171,7 @@
 typedef void (*fm_set_blnd_cb) (int status);
 typedef void (*fm_get_stn_prm_cb) (int val, int status);
 typedef void (*fm_get_stn_dbg_prm_cb) (int val, int status);
+typedef void (*fm_enable_slimbus_cb) (int status);
 
 typedef struct {
     size_t  size;
@@ -206,6 +207,7 @@
     fm_set_blnd_cb fm_set_blend_cb;
     fm_get_stn_prm_cb fm_get_station_param_cb;
     fm_get_stn_dbg_prm_cb fm_get_station_debug_param_cb;
+    fm_enable_slimbus_cb enable_slimbus_cb;
 } fm_hal_callbacks_t;
 
 /* Opcode OCF */
diff --git a/helium/radio_helium_hal.c b/helium/radio_helium_hal.c
index 1109ca3..a25d888 100644
--- a/helium/radio_helium_hal.c
+++ b/helium/radio_helium_hal.c
@@ -79,7 +79,6 @@
         return;
     }
     rsp = (struct hci_fm_conf_rsp *)ev_rsp;
-    hal->jni_cb->thread_evt_cb(0);
     radio_hci_req_complete(rsp->status);
     hal->jni_cb->enabled_cb();
     if (rsp->status == FM_HC_STATUS_SUCCESS)
@@ -394,6 +393,13 @@
     clear_all_bit(station_dbg_param_mask_flag);
 }
 
+static void hci_cc_enable_slimbus_rsp(char *ev_buff)
+{
+    ALOGV("%s status %d", __func__, ev_buff[0]);
+    hal->jni_cb->thread_evt_cb(0);
+    hal->jni_cb->enable_slimbus_cb(ev_buff[0]);
+}
+
 static inline void hci_cmd_complete_event(char *buff)
 {
     uint16_t opcode;
@@ -491,6 +497,10 @@
             hci_cc_dbg_param_rsp(pbuf);
             break;
 
+    case hci_diagnostic_cmd_op_pack(HCI_OCF_FM_ENABLE_SLIMBUS):
+            hci_cc_enable_slimbus_rsp(pbuf);
+            break;
+
 /*    case hci_common_cmd_op_pack(HCI_OCF_FM_GET_SPUR_TABLE):
             hci_cc_get_spur_tbl(buff);
             break;
diff --git a/jni/Android.mk b/jni/Android.mk
index d3df8b9..32382e8 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -27,5 +27,17 @@
 LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := init.qti.fm.sh
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_EXECUTABLES)
+
+LOCAL_INIT_RC := init.qti.fm.rc
+
+include $(BUILD_PREBUILT)
+
 endif # is-vendor-board-platform
 endif # BOARD_HAVE_QCOM_FM
diff --git a/jni/android_hardware_fm.cpp b/jni/android_hardware_fm.cpp
index 516e0e4..06d03ce 100644
--- a/jni/android_hardware_fm.cpp
+++ b/jni/android_hardware_fm.cpp
@@ -120,9 +120,11 @@
 typedef void (*fm_set_blnd_cb) (int status);
 typedef void (*fm_get_stn_prm_cb) (int val, int status);
 typedef void (*fm_get_stn_dbg_prm_cb) (int val, int status);
+typedef void (*fm_enable_sb_cb) (int status);
 
 static JNIEnv *mCallbackEnv = NULL;
 static jobject mCallbacksObj = NULL;
+static bool mCallbacksObjCreated = false;
 static jfieldID sCallbacksField;
 
 jclass javaClassRef;
@@ -150,6 +152,7 @@
 jmethodID method_setBlendCallback;
 jmethodID method_getStnParamCallback;
 jmethodID method_getStnDbgParamCallback;
+jmethodID method_enableSlimbusCallback;
 
 static bool checkCallbackThread() {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -163,12 +166,11 @@
 
 void fm_enabled_cb() {
     ALOGD("Entered %s", __func__);
-    if (mCallbackEnv != NULL) {
-        ALOGE("javaObjectRef creating");
-        jobject javaObjectRef =  mCallbackEnv->NewObject(javaClassRef, method_enableCallback);
-        mCallbacksObj = javaObjectRef;
-        ALOGE("javaObjectRef = %p mCallbackobject =%p \n",javaObjectRef,mCallbacksObj);
-    }
+
+    if (!checkCallbackThread())
+        return;
+
+    mCallbackEnv->CallVoidMethod(mCallbacksObj, method_enableCallback);
     ALOGD("exit  %s", __func__);
 }
 
@@ -396,6 +398,7 @@
         return;
 
     mCallbackEnv->CallVoidMethod(mCallbacksObj, method_disableCallback);
+    mCallbacksObjCreated = false;
 }
 
 void fm_peek_rsp_cb(char *peek_rsp) {
@@ -523,6 +526,24 @@
     mCallbackEnv->CallVoidMethod(mCallbacksObj, method_getStnDbgParamCallback, val, status);
 }
 
+static void fm_enable_slimbus_cb(int status)
+{
+    ALOGD("++fm_enable_slimbus_cb mCallbacksObjCreated: %d", mCallbacksObjCreated);
+
+    if (mCallbacksObjCreated == false) {
+        jobject javaObjectRef =  mCallbackEnv->NewObject(javaClassRef, method_enableSlimbusCallback);
+        mCallbacksObj = javaObjectRef;
+        mCallbacksObjCreated = true;
+        return;
+    }
+
+    if (!checkCallbackThread())
+        return;
+
+    mCallbackEnv->CallVoidMethod(mCallbacksObj, method_enableSlimbusCallback, status);
+    ALOGV("--fm_enable_slimbus_cb");
+}
+
 typedef struct {
    size_t  size;
 
@@ -557,6 +578,7 @@
    fm_set_blnd_cb fm_set_blend_cb;
    fm_get_stn_prm_cb fm_get_station_param_cb;
    fm_get_stn_dbg_prm_cb fm_get_station_debug_param_cb;
+   fm_enable_sb_cb fm_enable_slimbus_cb;
 } fm_vendor_callbacks_t;
 
 typedef struct {
@@ -598,7 +620,8 @@
     fm_def_data_write_cb,
     fm_set_blend_cb,
     fm_get_station_param_cb,
-    fm_get_station_debug_param_cb
+    fm_get_station_debug_param_cb,
+    fm_enable_slimbus_cb
 };
 #endif
 /* native interface */
@@ -1593,6 +1616,7 @@
     method_setBlendCallback = env->GetMethodID(javaClassRef, "setBlendCallback", "(I)V");
     method_getStnParamCallback = env->GetMethodID(javaClassRef, "getStnParamCallback", "(II)V");
     method_getStnDbgParamCallback = env->GetMethodID(javaClassRef, "getStnDbgParamCallback", "(II)V");
+    method_enableSlimbusCallback = env->GetMethodID(javaClassRef, "enableSlimbusCallback", "(I)V");
 
     return;
 error:
@@ -1621,6 +1645,7 @@
     mCallbacksObj = env->NewGlobalRef(object);
 #endif
 }
+
 static void cleanupNative(JNIEnv *env, jobject object) {
 
 #ifdef FM_SOC_TYPE_CHEROKEE
diff --git a/jni/init.qti.fm.rc b/jni/init.qti.fm.rc
new file mode 100644
index 0000000..5d52259
--- /dev/null
+++ b/jni/init.qti.fm.rc
@@ -0,0 +1,6 @@
+service fm_dl /vendor/bin/init.qti.fm.sh
+    class late_start
+    user system
+    group system
+    disabled
+    oneshot
diff --git a/jni/init.qti.fm.sh b/jni/init.qti.fm.sh
new file mode 100644
index 0000000..e484753
--- /dev/null
+++ b/jni/init.qti.fm.sh
@@ -0,0 +1,90 @@
+#!/vendor/bin/sh
+# Copyright (c) 2009-2011, 2015, 2017 The Linux Foundation. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#     * Neither the name of The Linux Foundation nor
+#       the names of its contributors may be used to endorse or promote
+#       products derived from this software without specific prior written
+#       permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+setprop hw.fm.init 0
+
+mode=`getprop hw.fm.mode`
+version=199217
+
+LOG_TAG="qti-fm"
+LOG_NAME="${0}:"
+
+loge ()
+{
+  /vendor/bin/log -t $LOG_TAG -p e "$LOG_NAME $@"
+}
+
+logi ()
+{
+  /vendor/bin/log -t $LOG_TAG -p i "$LOG_NAME $@"
+}
+
+failed ()
+{
+  loge "$1: exit code $2"
+  exit $2
+}
+
+logi "In FM shell Script"
+logi "mode: $mode"
+logi "Version : $version"
+
+#$fm_qsoc_patches <fm_chipVersion> <enable/disable WCM>
+#
+case $mode in
+  "normal")
+        logi "inserting the radio transport module"
+        echo 1 > /sys/module/radio_iris_transport/parameters/fmsmd_set
+        /vendor/bin/fm_qsoc_patches $version 0
+     ;;
+  "wa_enable")
+   /vendor/bin/fm_qsoc_patches $version 1
+     ;;
+  "wa_disable")
+   /vendor/bin/fm_qsoc_patches $version 2
+     ;;
+   *)
+    logi "Shell: Default case"
+    /vendor/bin/fm_qsoc_patches $version 0
+    ;;
+esac
+
+exit_code_fm_qsoc_patches=$?
+
+case $exit_code_fm_qsoc_patches in
+   0)
+    logi "FM QSoC calibration and firmware download succeeded"
+   ;;
+  *)
+    failed "FM QSoC firmware download and/or calibration failed" $exit_code_fm_qsoc_patches
+   ;;
+esac
+
+setprop hw.fm.init 1
+
+exit 0
diff --git a/qcom/fmradio/FmReceiverJNI.java b/qcom/fmradio/FmReceiverJNI.java
index d9fa79a..1487b7c 100644
--- a/qcom/fmradio/FmReceiverJNI.java
+++ b/qcom/fmradio/FmReceiverJNI.java
@@ -127,6 +127,13 @@
         FmReceiver.mCallback.FmRxGetStationDbgParam(val, status);
     }
 
+    public void enableSlimbusCallback(int status)
+    {
+        Log.d(TAG, "++enableSlimbusCallback" );
+        FmReceiver.mCallback.FmRxEvEnableSlimbus(status);
+        Log.d(TAG, "--enableSlimbusCallback" );
+    }
+
     public void RtPlusCallback(byte[] rtplus) {
         Log.d(TAG, "RtPlusCallback enter " );
         if (rtplus == null) {
diff --git a/qcom/fmradio/FmRxEvCallbacks.java b/qcom/fmradio/FmRxEvCallbacks.java
index 267d73d..31a8312 100644
--- a/qcom/fmradio/FmRxEvCallbacks.java
+++ b/qcom/fmradio/FmRxEvCallbacks.java
@@ -47,4 +47,5 @@
     public void FmRxEvRTPlus();
     public void FmRxEvERTInfo();
     public void FmRxEvECCInfo();
+    public void FmRxEvEnableSlimbus(int status);
 }
diff --git a/qcom/fmradio/FmRxEvCallbacksAdaptor.java b/qcom/fmradio/FmRxEvCallbacksAdaptor.java
index 59953e8..213cf43 100644
--- a/qcom/fmradio/FmRxEvCallbacksAdaptor.java
+++ b/qcom/fmradio/FmRxEvCallbacksAdaptor.java
@@ -61,4 +61,5 @@
     public void FmRxEvSetBlend(int status) {};
     public void FmRxGetStationParam(int val, int status) {};
     public void FmRxGetStationDbgParam(int val, int status) {};
+    public void FmRxEvEnableSlimbus(int status) {};
 }