AndroidComponentAddress includes a target UserHandle (#11670) · grpc/grpc-java@e58c998

@@ -18,10 +18,14 @@

18181919

import static android.content.Intent.URI_ANDROID_APP_SCHEME;

2020

import static com.google.common.base.Preconditions.checkArgument;

21+

import static com.google.common.base.Preconditions.checkState;

21222223

import android.content.ComponentName;

2324

import android.content.Context;

2425

import android.content.Intent;

26+

import android.os.UserHandle;

27+

import com.google.common.base.Objects;

28+

import io.grpc.ExperimentalApi;

2529

import java.net.SocketAddress;

2630

import javax.annotation.Nullable;

2731

@@ -41,18 +45,25 @@

4145

* fields, namely, an action of {@link ApiConstants#ACTION_BIND}, an empty category set and null

4246

* type and data URI.

4347

*

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.

4552

*/

4653

public final class AndroidComponentAddress extends SocketAddress {

4754

private static final long serialVersionUID = 0L;

48554956

private final Intent bindIntent; // "Explicit", having either a component or package restriction.

505751-

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) {

5262

checkArgument(

5363

bindIntent.getComponent() != null || bindIntent.getPackage() != null,

5464

"'bindIntent' must be explicit. Specify either a package or ComponentName.");

5565

this.bindIntent = bindIntent;

66+

this.targetUser = targetUser;

5667

}

57685869

/**

@@ -99,7 +110,7 @@ public static AndroidComponentAddress forRemoteComponent(

99110

* @throws IllegalArgumentException if 'intent' isn't "explicit"

100111

*/

101112

public static AndroidComponentAddress forBindIntent(Intent intent) {

102-

return new AndroidComponentAddress(intent.cloneFilter());

113+

return new AndroidComponentAddress(intent.cloneFilter(), null);

103114

}

104115105116

/**

@@ -108,7 +119,7 @@ public static AndroidComponentAddress forBindIntent(Intent intent) {

108119

*/

109120

public static AndroidComponentAddress forComponent(ComponentName component) {

110121

return new AndroidComponentAddress(

111-

new Intent(ApiConstants.ACTION_BIND).setComponent(component));

122+

new Intent(ApiConstants.ACTION_BIND).setComponent(component), null);

112123

}

113124114125

/**

@@ -141,6 +152,9 @@ public ComponentName getComponent() {

141152

/**

142153

* Returns this address as an explicit {@link Intent} suitable for passing to {@link

143154

* 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.

144158

*/

145159

public Intent asBindIntent() {

146160

return bindIntent.cloneFilter(); // Intent is mutable so return a copy.

@@ -177,13 +191,77 @@ public int hashCode() {

177191

public boolean equals(Object obj) {

178192

if (obj instanceof AndroidComponentAddress) {

179193

AndroidComponentAddress that = (AndroidComponentAddress) obj;

180-

return bindIntent.filterEquals(that.bindIntent);

194+

return bindIntent.filterEquals(that.bindIntent)

195+

&& Objects.equal(this.targetUser, that.targetUser);

181196

}

182197

return false;

183198

}

184199185200

@Override

186201

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+

}

188266

}

189267

}