content: /*
   * Copyright 2019 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.bluetooth;
  
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.os.IBinder;
  import android.os.RemoteException;
  import android.os.UserHandle;
  import android.util.Log;
  
  /**
   * Connector for Bluetooth profile proxies to bind manager service and
   * profile services
   * @param <T> The Bluetooth profile interface for this connection.
   * @hide
   */
  public abstract class BluetoothProfileConnector<T> {
      private final int mProfileId;
      private BluetoothProfile.ServiceListener mServiceListener;
      private final BluetoothProfile mProfileProxy;
      private Context mContext;
      private final String mProfileName;
      private final String mServiceName;
      private volatile T mService;
  
      private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
              new IBluetoothStateChangeCallback.Stub() {
          public void onBluetoothStateChange(boolean up) {
              if (up) {
                  doBind();
              } else {
                  doUnbind();
              }
          }
      };
  
      private final ServiceConnection mConnection = new ServiceConnection() {
          public void onServiceConnected(ComponentName className, IBinder service) {
              logDebug("Proxy object connected");
              mService = getServiceInterface(service);
  
              if (mServiceListener != null) {
                  mServiceListener.onServiceConnected(mProfileId, mProfileProxy);
              }
          }
  
          public void onServiceDisconnected(ComponentName className) {
              logDebug("Proxy object disconnected");
              doUnbind();
              if (mServiceListener != null) {
                  mServiceListener.onServiceDisconnected(mProfileId);
              }
          }
      };
  
      BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName,
              String serviceName) {
          mProfileId = profileId;
          mProfileProxy = profile;
          mProfileName = profileName;
          mServiceName = serviceName;
      }
  
      private boolean doBind() {
          synchronized (mConnection) {
              if (mService == null) {
                  logDebug("Binding service...");
                  try {
                      Intent intent = new Intent(mServiceName);
                      ComponentName comp = intent.resolveSystemService(
                              mContext.getPackageManager(), 0);
                      intent.setComponent(comp);
                      if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
                              UserHandle.CURRENT_OR_SELF)) {
                          logError("Could not bind to Bluetooth Service with " + intent);
                          return false;
                      }
                  } catch (SecurityException se) {
                      logError("Failed to bind service. " + se);
                      return false;
                  }
              }
          }
          return true;
      }
  
      private void doUnbind() {
          synchronized (mConnection) {
              if (mService != null) {
                  logDebug("Unbinding service...");
                  try {
                      mContext.unbindService(mConnection);
                  } catch (IllegalArgumentException ie) {
                      logError("Unable to unbind service: " + ie);
                  } finally {
                      mService = null;
                  }
              }
          }
      }
  
      void connect(Context context, BluetoothProfile.ServiceListener listener) {
          mContext = context;
          mServiceListener = listener;
          IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
          if (mgr != null) {
              try {
                  mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
              } catch (RemoteException re) {
                  logError("Failed to register state change callback. " + re);
              }
          }
          doBind();
      }
  
      void disconnect() {
          mServiceListener = null;
          IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
          if (mgr != null) {
              try {
                  mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
              } catch (RemoteException re) {
                  logError("Failed to unregister state change callback" + re);
              }
          }
          doUnbind();
      }
  
      T getService() {
          return mService;
      }
  
      /**
       * This abstract function is used to implement method to get the
       * connected Bluetooth service interface.
       * @param service the connected binder service.
       * @return T the binder interface of {@code service}.
       * @hide
       */
      public abstract T getServiceInterface(IBinder service);
  
      private void logDebug(String log) {
          Log.d(mProfileName, log);
      }
  
      private void logError(String log) {
          Log.e(mProfileName, log);
      }
  }
  