-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
AndroidComponentAddress.java
162 lines (144 loc) · 6.2 KB
/
AndroidComponentAddress.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
* 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 android.content.Intent.URI_ANDROID_APP_SCHEME;
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} in another
* application and using the default binding {@link Intent}.
*
* @param applicationPackage The package name of the application containing the server.
* @param serviceClassName The full class name of the Android Service to bind to.
*/
public static AndroidComponentAddress forRemoteComponent(
String applicationPackage, String serviceClassName) {
return forComponent(new ComponentName(applicationPackage, serviceClassName));
}
/**
* 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.
}
/**
* Returns this address as an "android-app://" uri.
*
* <p>See {@link Intent#URI_ANDROID_APP_SCHEME} for details.
*/
public String asAndroidAppUri() {
Intent intentForUri = bindIntent;
if (intentForUri.getPackage() == null) {
// URI_ANDROID_APP_SCHEME requires an "explicit package name" which isn't set by any of our
// factory methods. Oddly, our explicit ComponentName is not enough.
intentForUri = intentForUri.cloneFilter().setPackage(getComponent().getPackageName());
}
return intentForUri.toUri(URI_ANDROID_APP_SCHEME);
}
@Override
public int hashCode() {
Intent intentForHashCode = bindIntent;
// Clear a (usually redundant) package filter to work around an Android >= 31 bug where certain
// Intents compare filterEquals() but have different filterHashCode() values. It's always safe
// to include fewer fields in the hashCode() computation.
if (intentForHashCode.getPackage() != null) {
intentForHashCode = intentForHashCode.cloneFilter().setPackage(null);
}
return intentForHashCode.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 + "]";
}
}