title: how to get rid of Activity has leaked Service Connection error
body: <p>The logcat is showing an Activity has leaked Service Connection error after onDestroy() gets called. Here is a section of the logcat:  </p>
  
  <pre><code>01-13 18:14:50.281: D/MYLOG(16984): in passStop()
  01-13 18:14:51.471: D/MYLOG(16984): onPause() called
  01-13 18:14:52.001: D/MYLOG(16984): onDestroy() called
  01-13 18:14:52.021: E/ActivityThread(16984): Activity com.example.mypassthrough.MainActivity has leaked ServiceConnection android.bluetooth.BluetoothA2dp$2@41e8cd68 that was originally bound here
  01-13 18:14:52.021: E/ActivityThread(16984): android.app.ServiceConnectionLeaked: Activity com.example.mypassthrough.MainActivity has leaked ServiceConnection android.bluetooth.BluetoothA2dp$2@41e8cd68 that was originally bound here
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.app.LoadedApk$ServiceDispatcher.&lt;init&gt;(LoadedApk.java:970)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:864)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1569)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.app.ContextImpl.bindService(ContextImpl.java:1552)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.content.ContextWrapper.bindService(ContextWrapper.java:517)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.bluetooth.BluetoothA2dp.doBind(BluetoothA2dp.java:165)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.bluetooth.BluetoothA2dp.&lt;init&gt;(BluetoothA2dp.java:158)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.bluetooth.BluetoothAdapter.getProfileProxy(BluetoothAdapter.java:1364)
  01-13 18:14:52.021: E/ActivityThread(16984):    at com.example.mypassthrough.BluetoothA2DPRequester.request(BluetoothA2DPRequester.java:52)
  01-13 18:14:52.021: E/ActivityThread(16984):    at com.example.mypassthrough.MainActivity.btnA2dpStart_onClick(MainActivity.java:307)
  01-13 18:14:52.021: E/ActivityThread(16984):    at java.lang.reflect.Method.invokeNative(Native Method)
  01-13 18:14:52.021: E/ActivityThread(16984):    at java.lang.reflect.Method.invoke(Method.java:515)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.view.View$1.onClick(View.java:3818)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.view.View.performClick(View.java:4438)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.view.View$PerformClick.run(View.java:18422)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.os.Handler.handleCallback(Handler.java:733)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.os.Handler.dispatchMessage(Handler.java:95)
  01-13 18:14:52.021: E/ActivityThread(16984):    at android.os.Looper.loop(Looper.java:136)  
  </code></pre>
  
  <p>This is the MainActivity file:  </p>
  
  <pre><code>// public class MainActivity extends Activity {
  // public class MainActivity extends Activity implements BluetoothBroadcastReceiver.Callback, BluetoothA2DPRequester.Callback {
  public class MainActivity extends Activity implements BluetoothA2DPRequester.Callback {  
  
      AudioManager am = null;
      AudioRecord record =null;
      AudioTrack track =null;
      BluetoothHeadset mBluetoothHeadset;
      // BluetoothA2dp mBluetoothSpeaker;
      BluetoothDevice device;
      BluetoothDevice mConnectedHeadset;
  
      ConnectionClass mConnectionClass;
  
      final int SAMPLE_FREQUENCY = 16000;
      final int SIZE_OF_RECORD_ARRAY = 1024;  // 1024 ORIGINAL
      final int WAV_SAMPLE_MULTIPLICATION_FACTOR = 1;
      int i= 0;
      boolean isPlaying = false;
  
      // Local Bluetooth adapter
      private BluetoothAdapter mBluetoothAdapter = null;
  
      // Intent request codes
      private static final int REQUEST_CONNECT_DEVICE = 1;
      private static final int REQUEST_ENABLE_BT = 2;
  
      private static final String HTC_MEDIA = "CSR8670-CNS 10001v4-STEREO";
  
  
  
      class MyThread extends Thread{
          private volatile boolean passThroughMode = true;
  
          MyThread(){
              super();
          }
  
          MyThread(boolean newPTV){
              this.passThroughMode = newPTV;
          }
  
          @Override
          public void run(){
              short[] lin = new short[SIZE_OF_RECORD_ARRAY];
              int num = 0;
              // record.startRecording();
              // track.play();
              while (passThroughMode) {
              // while (!isInterrupted()) {
                  num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY);
                  for(i=0;i&lt;lin.length;i++)
                      lin[i] *= WAV_SAMPLE_MULTIPLICATION_FACTOR; 
                  track.write(lin, 0, num);
              }
  
              record.stop();
              track.stop();
  //          record.release();
  //          track.release();
          }
  
          public void stopThread(){
              passThroughMode = false;
          }
  
          boolean getpassThroughMode(){
              return passThroughMode;
          }
  
          void setpassThroughMode(boolean value){
              this.passThroughMode = value;
          }
  
      }   // class MyThread extends Thread{   CLOSED
  
      MyThread newThread;
  
      private void init() {
          Log.d("MYLOG", "MainActivity init() called");
          int min = AudioRecord.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
          record = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO,
                                   AudioFormat.ENCODING_PCM_16BIT, min);
          int maxJitter = AudioTrack.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
          track = new AudioTrack(AudioManager.MODE_IN_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO,
                                 AudioFormat.ENCODING_PCM_16BIT, maxJitter, AudioTrack.MODE_STREAM);
          am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
          am.setMode(AudioManager.MODE_IN_COMMUNICATION);
  
      }
  
      private void ensureDiscoverable() {
          Log.d("MYLOG", "MainActivity ensure discoverable");
          if (mBluetoothAdapter.getScanMode() !=
              BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
              Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
              discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
              startActivity(discoverableIntent);
          }
      }
  
  
      @SuppressLint({ "InlinedApi", "NewApi" })
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          setVolumeControlStream(AudioManager.MODE_IN_COMMUNICATION);
          // init(); // -&gt; Moved this to onResume();
          Log.d("MYLOG", "MainActivity onCreate() called");
          // Get local Bluetooth adapter
          mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  
          // If the adapter is null, then Bluetooth is not supported
          if (mBluetoothAdapter == null) {
              Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
              finish();
              // return;
          }
          Log.d("MYLOG", "getProfileProxy() called");
  
      }
  
      @Override
      public void onStart() {
          super.onStart();
          Log.e("MYLOG", "++ ON START ++");
  
          // If BT is not on, request that it be enabled.
          // setupChat() will then be called during onActivityResult
          if (!mBluetoothAdapter.isEnabled()) {
              Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
              startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
              // Otherwise, setup the chat session
          } 
  
          // mConnectionClass = new ConnectionClass();
          mConnectionClass = new ConnectionClass(this);
       }
  
      @Override
      protected void onResume(){
          super.onResume();
          // newThread.stopThread();
          Log.d("MYLOG", "onResume() called");
          init();
          newThread = new MyThread(true);         // ORIGINAL -&gt; true
          newThread.start(); 
      }
  
      @Override
      protected void onPause(){
          super.onPause();
          Log.d("MYLOG", "onPause() called");
          newThread.stopThread();
      }
  
  
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          // Inflate the menu; this adds items to the action bar if it is present.
          getMenuInflater().inflate(R.menu.main, menu);
          return true;
      }
  
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          switch (item.getItemId()) {
          case R.id.scan:
              // Launch the DeviceListActivity to see devices and do scan
              Intent serverIntent = new Intent(this, DeviceListActivity.class);
              startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
              return true;
          case R.id.discoverable:
              // Ensure this device is discoverable by others
              ensureDiscoverable();
              return true;
          }
          return false;
      }   
  
      public void onActivityResult(int requestCode, int resultCode, Intent data) {
          Log.d("MYLOG", "MainActivity onActivityResult " + resultCode);
          switch (requestCode) {
          case REQUEST_CONNECT_DEVICE:
              // When DeviceListActivity returns with a device to connect
              if (resultCode == Activity.RESULT_OK) {
                  // Get the device MAC address
                  String address = data.getExtras()
                                       .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
                  // Get the BLuetoothDevice object
                  // BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
                  device = mBluetoothAdapter.getRemoteDevice(address);
                  // Attempt to connect to the device
                  // mChatService.connect(device);
                  Log.d("MYLOG", "ABOUT TO CONNECT");
                  mConnectionClass.connect(device);
              }
              break;
          case REQUEST_ENABLE_BT:
              // When the request to enable Bluetooth returns
              if (resultCode == Activity.RESULT_OK) {
                  // Bluetooth is now enabled, so set up a chat session
                  // setupChat();
                  Toast.makeText(this, "BLUETOOTH ENABLED", Toast.LENGTH_LONG).show();
              } else {
                  // User did not enable Bluetooth or an error occured
                  Log.d("MYLOG", "BT not enabled");
                  Toast.makeText(this, "BLUETOOTH NOT ENABLED", Toast.LENGTH_LONG).show();
                  // finish();
              }
          }
      }
  
  
      public void passStop(View view){
          Button playBtn = (Button) findViewById(R.id.playBtn);
          Log.d("MYLOG", "in passStop()");
  
          if(!isPlaying){
              record.startRecording();
              track.play();
              isPlaying = true;
              playBtn.setText("Pause");
          }
          else{
             record.stop();
             track.pause();
             isPlaying=false;
             playBtn.setText("Start");
          }
      }
  
  
      public void btnA2dpStart_onClick(View view){
  //      onBluetoothConnected();
          new BluetoothA2DPRequester(this).request(this, mBluetoothAdapter);
      }
  
      @Override
      protected void onDestroy() {
          super.onDestroy();
          newThread.stopThread();
          record.release();
          track.release();
  //      unregisterReceiver(mReceiver);
          // mBluetoothAdapter.disable();
           Log.d("MYLOG", "onDestroy() called");
      }
  
      @Override
      public void onA2DPProxyReceived (BluetoothA2dp proxy) {
          Log.d("MYLOG", "MainActivity onA2DPProxyReceived(BluetoothA2dp proxy) called");
          Method connect = getConnectMethod();
          BluetoothDevice device = findBondedDeviceByName(mBluetoothAdapter, HTC_MEDIA);
  
          //If either is null, just return. The errors have already been logged
          if (connect == null || device == null) {
              return;
          }
  
          try {
              connect.setAccessible(true);
              connect.invoke(proxy, device);
          } catch (InvocationTargetException ex) {
              Log.e("MYLOG", "Unable to invoke connect(BluetoothDevice) method on proxy. " + ex.toString());
          } catch (IllegalAccessException ex) {
              Log.e("MYLOG", "Illegal Access! " + ex.toString());
          }
      }
  
      /**
       * Wrapper around some reflection code to get the hidden 'connect()' method
       * @return the connect(BluetoothDevice) method, or null if it could not be found
       */
      @SuppressLint("NewApi")
      private Method getConnectMethod () {
          Log.d("MYLOG", "MainActivity getConnectMethod() called");
          try {
              return BluetoothA2dp.class.getDeclaredMethod("connect", BluetoothDevice.class);
          } catch (NoSuchMethodException ex) {
              Log.e("MYLOG", "Unable to find connect(BluetoothDevice) method in BluetoothA2dp proxy.");
              return null;
          }
      }
  
      /**
       * Search the set of bonded devices in the BluetoothAdapter for one that matches
       * the given name
       * @param adapter the BluetoothAdapter whose bonded devices should be queried
       * @param name the name of the device to search for
       * @return the BluetoothDevice by the given name (if found); null if it was not found
       */
      private static BluetoothDevice findBondedDeviceByName (BluetoothAdapter adapter, String name) {
          Log.d("MYLOG", "MainActivity findBondedDeviceByName(BluetoothAdapter adapter, String name) called");
          for (BluetoothDevice device : getBondedDevices(adapter)) {
              if (name.matches(device.getName())) {
                  Log.v("MYLOG", String.format("Found device with name %s and address %s.", device.getName(), device.getAddress()));
                  return device;
              }
          }
          Log.w("MYLOG", String.format("Unable to find device with name %s.", name));
          return null;
      }
  
      /**
       * Safety wrapper around BluetoothAdapter#getBondedDevices() that is guaranteed
       * to return a non-null result
       * @param adapter the BluetoothAdapter whose bonded devices should be obtained
       * @return the set of all bonded devices to the adapter; an empty set if there was an error
       */
      private static Set&lt;BluetoothDevice&gt; getBondedDevices (BluetoothAdapter adapter) {
          Log.d("MYLOG", "MainActivity getBondedDevices(BluetoothAdapter adapter) called");
          Set&lt;BluetoothDevice&gt; results = adapter.getBondedDevices();
          if (results == null) {
              results = new HashSet&lt;BluetoothDevice&gt;();
          }
          return results;
      }   
  
  }
  </code></pre>
  
  <p>The BluetoothA2DPRequester class is here:  </p>
  
  <pre><code>@SuppressLint("NewApi")
  public class BluetoothA2DPRequester implements BluetoothProfile.ServiceListener {
      private Callback mCallback;
  
      /**
       * Creates a new instance of an A2DP Proxy requester with the
       * callback that should receive the proxy once it is acquired
       * @param callback the callback that should receive the proxy
       */
      public BluetoothA2DPRequester(Callback callback) {
          Log.d("MYLOG", "BluetoothA2DPRequester Constructor; Action: mCallback = callback");
          mCallback = callback;
      }
  
      /**
       * Start an asynchronous request to acquire the A2DP proxy. The callback
       * will be notified when the proxy is acquired
       * @param c the context used to obtain the proxy
       * @param adapter the BluetoothAdapter that should receive the request for proxy
       */
      @SuppressLint("NewApi")
      public void request (Context c, BluetoothAdapter adapter) {
          Log.d("MYLOG", "BluetoothA2DPRequester request(Context c, BluetoothAdapter adapter) called");
          adapter.getProfileProxy(c, this, BluetoothProfile.A2DP);
      }
  
      @Override
      public void onServiceConnected(int i, BluetoothProfile bluetoothProfile) {
          Log.d("MYLOG", "BluetoothA2DPRequester onServiceConnected(int i, BluetoothProfile bluetoothProfile) called");
          if (mCallback != null) {
              mCallback.onA2DPProxyReceived((BluetoothA2dp) bluetoothProfile);
          }
      }
  
      @Override
      public void onServiceDisconnected(int i) {
          //It's a one-off connection attempt; we don't care about the disconnection event.
      }
  
      public static interface Callback {
          public void onA2DPProxyReceived (BluetoothA2dp proxy);
      }
  }
  </code></pre>
  
  <p>which I obtained from <a href="https://stackoverflow.com/a/18397935/1693203">here</a>.  </p>
  
  <p>Why is the service connection leaked error happening and how do I fix it? It only shows up after the onDestroy and I haven't seen the app crash due to it (yet). Can it be safely ignored?  </p>
  
  <p>My guess:  </p>
  
  <p>The BluetoothA2DPRequester class implements BluetoothProfile.ServiceListener and when a nameless temporary object of this class is made here  </p>
  
  <pre><code>    public void btnA2dpStart_onClick(View view){
  //      onBluetoothConnected();
          new BluetoothA2DPRequester(this).request(this, mBluetoothAdapter);
      }
  </code></pre>
  
  <p>I am assuming that a service listener gets registered to this class and is causing this error. is this the case? If it is, how do I unregister it, since the service listener has no name?</p>