blob: 9fd400c0b76efd09a435800ea4c14ec4ca4784a1 [file] [log] [blame]
/*
* Copyright 2020 The gRPC Authors
*
* 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 io.grpc.binder;
import static com.google.common.base.Preconditions.checkArgument;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import io.grpc.ExperimentalApi;
import java.net.SocketAddress;
/**
* The target of an Android {@link android.app.Service} binding.
*
* <p>Consists of a {@link ComponentName} reference to the Service and the action, data URI, type,
* and category set for an {@link Intent} used to bind to it. All together, these fields identify
* the {@link android.os.IBinder} that would be returned by some implementation of {@link
* android.app.Service#onBind(Intent)}. Indeed, the semantics of {@link #equals(Object)} match
* Android's internal equivalence relation for caching the result of calling this method. See <a
* href="https://developer.android.com/guide/components/bound-services">Bound Services Overview</a>
* for more.
*
* <p>For convenience in the common case where a {@link android.app.Service} exposes just one {@link
* android.os.IBinder} IPC interface, we provide default values for the binding {@link Intent}
* fields, namely, an action of {@link ApiConstants#ACTION_BIND}, an empty category set and null
* type and data URI.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/8022")
public class AndroidComponentAddress extends SocketAddress { // NOTE: Only temporarily non-final.
private static final long serialVersionUID = 0L;
private final Intent bindIntent; // An "explicit" Intent. In other words, getComponent() != null.
protected AndroidComponentAddress(Intent bindIntent) {
checkArgument(bindIntent.getComponent() != null, "Missing required component");
this.bindIntent = bindIntent;
}
/**
* Creates an address for the given {@link android.app.Service} instance with the default binding
* {@link Intent}.
*/
public static AndroidComponentAddress forContext(Context context) {
return forLocalComponent(context, context.getClass());
}
/**
* Creates an address referencing a {@link android.app.Service} hosted by this application and
* using the default binding {@link Intent}.
*/
public static AndroidComponentAddress forLocalComponent(Context context, Class<?> cls) {
return forComponent(new ComponentName(context, cls));
}
/**
* Creates an address referencing a {@link android.app.Service} (potentially) in another
* application and using the default binding {@link Intent}.
*/
public static AndroidComponentAddress forRemoteComponent(String packageName, String className) {
return forComponent(new ComponentName(packageName, className));
}
/**
* Creates a new address that refers to <code>intent</code>'s component and that uses the "filter
* matching" fields of <code>intent</code> as the binding {@link Intent}.
*
* <p>A multi-tenant {@link android.app.Service} can call this from its {@link
* android.app.Service#onBind(Intent)} method to locate an appropriate {@link io.grpc.Server} by
* listening address.
*
* @throws IllegalArgumentException if intent's component is null
*/
public static AndroidComponentAddress forBindIntent(Intent intent) {
return new AndroidComponentAddress(intent.cloneFilter());
}
/**
* Creates an address referencing the specified {@link android.app.Service} component and using
* the default binding {@link Intent}.
*/
public static AndroidComponentAddress forComponent(ComponentName component) {
return new AndroidComponentAddress(
new Intent(ApiConstants.ACTION_BIND).setComponent(component));
}
public String getAuthority() {
return getComponent().getPackageName();
}
public ComponentName getComponent() {
return bindIntent.getComponent();
}
/**
* Returns this address as an explicit {@link Intent} suitable for passing to {@link
* Context#bindService}.
*/
public Intent asBindIntent() {
return bindIntent.cloneFilter(); // Intent is mutable so return a copy.
}
@Override
public int hashCode() {
return bindIntent.filterHashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AndroidComponentAddress) {
AndroidComponentAddress that = (AndroidComponentAddress) obj;
return bindIntent.filterEquals(that.bindIntent);
}
return false;
}
@Override
public String toString() {
return "AndroidComponentAddress[" + bindIntent + "]";
}
}