content: /*
   * Copyright (C) 2020 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 androidx.test.internal.events.client;
  
  import static androidx.test.internal.util.Checks.checkNotNull;
  
  import android.app.Service;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.os.IBinder;
  import android.os.IInterface;
  import android.util.Log;
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.annotation.VisibleForTesting;
  
  /**
   * The base class that service connections have to extend. Handles connection to the service proxy
   * and callbacks to the caller. Extended by {@link TestDiscoveryEventServiceConnection} and {@link
   * TestRunEventServiceConnection}.
   */
  public class TestEventServiceConnectionBase<T extends IInterface>
      implements TestEventServiceConnection {
    private static final String TAG = "ConnectionBase";
  
    @NonNull private final TestEventClientConnectListener listener;
    @NonNull private final ServiceFromBinder<T> serviceFromBinder;
    @NonNull private final String serviceName;
    @Nullable private final String servicePackageName;
    @Nullable public T service = null;
  
    /** An interface to match the signature of {@link IInterface#asBinder()}. */
    public interface ServiceFromBinder<T extends IInterface> {
      T asInterface(IBinder binder);
    }
  
    private final ServiceConnection connection =
        new ServiceConnection() {
  
          @Override
          public void onServiceConnected(ComponentName className, IBinder binder) {
            TestEventServiceConnectionBase.this.service = serviceFromBinder.asInterface(binder);
            Log.d(TAG, "Connected to " + serviceName);
            // Notify the caller e.g. {@code AndroidJunitRunner} to start instrumentation since
            // service connection succeeded.
            listener.onTestEventClientConnect();
          }
  
          @Override
          public void onServiceDisconnected(ComponentName className) {
            service = null;
            Log.d(TAG, "Disconnected from " + serviceName);
          }
        };
  
    /** Initializes a new Orchestrator service connection. */
    public TestEventServiceConnectionBase(
        @NonNull String serviceName,
        @NonNull ServiceFromBinder<T> serviceFromBinder,
        @NonNull TestEventClientConnectListener listener) {
      this.serviceName = checkNotNull(getServiceNameOnly(serviceName), "serviceName cannot be null");
      this.servicePackageName =
          checkNotNull(getServicePackage(serviceName), "servicePackageName cannot be null");
      this.listener = checkNotNull(listener, "listener cannot be null");
      ;
      this.serviceFromBinder = checkNotNull(serviceFromBinder, "serviceFromBinder cannot be null");
    }
  
    /** {@inheritDoc} */
    @Override
    public void connect(@NonNull Context context) {
      Intent intent = new Intent(serviceName);
      intent.setPackage(servicePackageName);
      if (!context.bindService(intent, connection, Service.BIND_AUTO_CREATE)) {
        throw new IllegalStateException("Cannot connect to " + serviceName);
      }
    }
  
    /**
     * Splits the package name and service name parts from a string in the format
     * "com.sample.package/.foo.Service". The package name is optional. If the service name starts
     * with '.' then the package name is prepended to get the full service class name.
     */
    @NonNull
    @VisibleForTesting
    static String getServiceNameOnly(@NonNull String serviceName) {
      String[] parts = serviceName.split("/");
      if (parts.length == 2) {
        return parts[1].startsWith(".") ? parts[0] + parts[1] : parts[1];
      } else if (parts.length == 1) {
        return parts[0];
      } else {
        throw new IllegalArgumentException("Invalid serviceName [" + serviceName + "]");
      }
    }
  
    @Nullable
    @VisibleForTesting
    static String getServicePackage(@NonNull String serviceName) {
      String[] parts = serviceName.split("/");
      return parts.length >= 2 ? parts[0] : null;
    }
  }
  