18
18
19
19
import static android .content .Intent .URI_ANDROID_APP_SCHEME ;
20
20
import static com .google .common .base .Preconditions .checkArgument ;
21
+ import static com .google .common .base .Preconditions .checkState ;
21
22
22
23
import android .content .ComponentName ;
23
24
import android .content .Context ;
24
25
import android .content .Intent ;
26
+ import android .os .UserHandle ;
27
+ import com .google .common .base .Objects ;
28
+ import io .grpc .ExperimentalApi ;
25
29
import java .net .SocketAddress ;
26
30
import javax .annotation .Nullable ;
27
31
41
45
* fields, namely, an action of {@link ApiConstants#ACTION_BIND}, an empty category set and null
42
46
* type and data URI.
43
47
*
44
- * <p>The semantics of {@link #equals(Object)} are the same as {@link Intent#filterEquals(Intent)}.
48
+ * <p>Optionally contains a {@link UserHandle} that must be considered wherever the {@link Intent}
49
+ * is evaluated.
50
+ *
51
+ * <p>{@link #equals(Object)} uses {@link Intent#filterEquals(Intent)} semantics to compare Intents.
45
52
*/
46
53
public final class AndroidComponentAddress extends SocketAddress {
47
54
private static final long serialVersionUID = 0L ;
48
55
49
56
private final Intent bindIntent ; // "Explicit", having either a component or package restriction.
50
57
51
- protected AndroidComponentAddress (Intent bindIntent ) {
58
+ @ Nullable
59
+ private final UserHandle targetUser ; // null means the same user that hosts this process.
60
+
61
+ protected AndroidComponentAddress (Intent bindIntent , @ Nullable UserHandle targetUser ) {
52
62
checkArgument (
53
63
bindIntent .getComponent () != null || bindIntent .getPackage () != null ,
54
64
"'bindIntent' must be explicit. Specify either a package or ComponentName." );
55
65
this .bindIntent = bindIntent ;
66
+ this .targetUser = targetUser ;
56
67
}
57
68
58
69
/**
@@ -99,7 +110,7 @@ public static AndroidComponentAddress forRemoteComponent(
99
110
* @throws IllegalArgumentException if 'intent' isn't "explicit"
100
111
*/
101
112
public static AndroidComponentAddress forBindIntent (Intent intent ) {
102
- return new AndroidComponentAddress (intent .cloneFilter ());
113
+ return new AndroidComponentAddress (intent .cloneFilter (), null );
103
114
}
104
115
105
116
/**
@@ -108,7 +119,7 @@ public static AndroidComponentAddress forBindIntent(Intent intent) {
108
119
*/
109
120
public static AndroidComponentAddress forComponent (ComponentName component ) {
110
121
return new AndroidComponentAddress (
111
- new Intent (ApiConstants .ACTION_BIND ).setComponent (component ));
122
+ new Intent (ApiConstants .ACTION_BIND ).setComponent (component ), null );
112
123
}
113
124
114
125
/**
@@ -141,6 +152,9 @@ public ComponentName getComponent() {
141
152
/**
142
153
* Returns this address as an explicit {@link Intent} suitable for passing to {@link
143
154
* Context#bindService}.
155
+ *
156
+ * <p>NB: The returned Intent does not specify a target Android user. If {@link #getTargetUser()}
157
+ * is non-null, {@link Context#bindServiceAsUser} should be called instead.
144
158
*/
145
159
public Intent asBindIntent () {
146
160
return bindIntent .cloneFilter (); // Intent is mutable so return a copy.
@@ -177,13 +191,77 @@ public int hashCode() {
177
191
public boolean equals (Object obj ) {
178
192
if (obj instanceof AndroidComponentAddress ) {
179
193
AndroidComponentAddress that = (AndroidComponentAddress ) obj ;
180
- return bindIntent .filterEquals (that .bindIntent );
194
+ return bindIntent .filterEquals (that .bindIntent )
195
+ && Objects .equal (this .targetUser , that .targetUser );
181
196
}
182
197
return false ;
183
198
}
184
199
185
200
@ Override
186
201
public String toString () {
187
- return "AndroidComponentAddress[" + bindIntent + "]" ;
202
+ StringBuilder builder = new StringBuilder ("AndroidComponentAddress[" );
203
+ if (targetUser != null ) {
204
+ builder .append (targetUser );
205
+ builder .append ("@" );
206
+ }
207
+ builder .append (bindIntent );
208
+ builder .append ("]" );
209
+ return builder .toString ();
210
+ }
211
+
212
+ /**
213
+ * Identifies the Android user in which the bind Intent will be evaluated.
214
+ *
215
+ * <p>Returns the {@link UserHandle}, or null which means that the Android user hosting the
216
+ * current process will be used.
217
+ */
218
+ @ ExperimentalApi ("https://github.com/grpc/grpc-java/issues/10173" )
219
+ @ Nullable
220
+ public UserHandle getTargetUser () {
221
+ return targetUser ;
222
+ }
223
+
224
+ public static Builder newBuilder () {
225
+ return new Builder ();
226
+ }
227
+
228
+ /** Fluently builds instances of {@link AndroidComponentAddress}. */
229
+ public static class Builder {
230
+ Intent bindIntent ;
231
+ UserHandle targetUser ;
232
+
233
+ /**
234
+ * Sets the binding {@link Intent} to one having the "filter matching" fields of 'intent'.
235
+ *
236
+ * <p>'intent' must be "explicit", i.e. having either a target component ({@link
237
+ * Intent#getComponent()}) or package restriction ({@link Intent#getPackage()}).
238
+ */
239
+ public Builder setBindIntent (Intent intent ) {
240
+ this .bindIntent = intent .cloneFilter ();
241
+ return this ;
242
+ }
243
+
244
+ /**
245
+ * Sets the binding {@link Intent} to one with the specified 'component' and default values for
246
+ * all other fields, for convenience.
247
+ */
248
+ public Builder setBindIntentFromComponent (ComponentName component ) {
249
+ this .bindIntent = new Intent (ApiConstants .ACTION_BIND ).setComponent (component );
250
+ return this ;
251
+ }
252
+
253
+ /** See {@link AndroidComponentAddress#getTargetUser()}. */
254
+ @ ExperimentalApi ("https://github.com/grpc/grpc-java/issues/10173" )
255
+ public Builder setTargetUser (@ Nullable UserHandle targetUser ) {
256
+ this .targetUser = targetUser ;
257
+ return this ;
258
+ }
259
+
260
+ public AndroidComponentAddress build () {
261
+ // We clone any incoming mutable intent in the setter, not here. AndroidComponentAddress
262
+ // itself is immutable so multiple instances built from here can safely share 'bindIntent'.
263
+ checkState (bindIntent != null , "Required property 'bindIntent' unset" );
264
+ return new AndroidComponentAddress (bindIntent , targetUser );
265
+ }
188
266
}
189
267
}
0 commit comments