• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.binder;
18 
19 import static android.content.Intent.URI_ANDROID_APP_SCHEME;
20 import static com.google.common.base.Preconditions.checkArgument;
21 
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import java.net.SocketAddress;
26 
27 /**
28  * The target of an Android {@link android.app.Service} binding.
29  *
30  * <p>Consists of a {@link ComponentName} reference to the Service and the action, data URI, type,
31  * and category set for an {@link Intent} used to bind to it. All together, these fields identify
32  * the {@link android.os.IBinder} that would be returned by some implementation of {@link
33  * android.app.Service#onBind(Intent)}. Indeed, the semantics of {@link #equals(Object)} match
34  * Android's internal equivalence relation for caching the result of calling this method. See <a
35  * href="https://developer.android.com/guide/components/bound-services">Bound Services Overview</a>
36  * for more.
37  *
38  * <p>For convenience in the common case where a {@link android.app.Service} exposes just one {@link
39  * android.os.IBinder} IPC interface, we provide default values for the binding {@link Intent}
40  * fields, namely, an action of {@link ApiConstants#ACTION_BIND}, an empty category set and null
41  * type and data URI.
42  */
43 public final class AndroidComponentAddress extends SocketAddress {
44   private static final long serialVersionUID = 0L;
45 
46   private final Intent bindIntent; // An "explicit" Intent. In other words, getComponent() != null.
47 
AndroidComponentAddress(Intent bindIntent)48   protected AndroidComponentAddress(Intent bindIntent) {
49     checkArgument(bindIntent.getComponent() != null, "Missing required component");
50     this.bindIntent = bindIntent;
51   }
52 
53   /**
54    * Creates an address for the given {@link android.app.Service} instance with the default binding
55    * {@link Intent}.
56    */
forContext(Context context)57   public static AndroidComponentAddress forContext(Context context) {
58     return forLocalComponent(context, context.getClass());
59   }
60 
61   /**
62    * Creates an address referencing a {@link android.app.Service} hosted by this application and
63    * using the default binding {@link Intent}.
64    */
forLocalComponent(Context context, Class<?> cls)65   public static AndroidComponentAddress forLocalComponent(Context context, Class<?> cls) {
66     return forComponent(new ComponentName(context, cls));
67   }
68 
69   /**
70    * Creates an address referencing a {@link android.app.Service} in another
71    * application and using the default binding {@link Intent}.
72    *
73    * @param applicationPackage The package name of the application containing the server.
74    * @param serviceClassName The full class name of the Android Service to bind to.
75    */
forRemoteComponent( String applicationPackage, String serviceClassName)76   public static AndroidComponentAddress forRemoteComponent(
77       String applicationPackage, String serviceClassName) {
78     return forComponent(new ComponentName(applicationPackage, serviceClassName));
79   }
80 
81   /**
82    * Creates a new address that refers to <code>intent</code>'s component and that uses the "filter
83    * matching" fields of <code>intent</code> as the binding {@link Intent}.
84    *
85    * <p>A multi-tenant {@link android.app.Service} can call this from its {@link
86    * android.app.Service#onBind(Intent)} method to locate an appropriate {@link io.grpc.Server} by
87    * listening address.
88    *
89    * @throws IllegalArgumentException if intent's component is null
90    */
forBindIntent(Intent intent)91   public static AndroidComponentAddress forBindIntent(Intent intent) {
92     return new AndroidComponentAddress(intent.cloneFilter());
93   }
94 
95   /**
96    * Creates an address referencing the specified {@link android.app.Service} component and using
97    * the default binding {@link Intent}.
98    */
forComponent(ComponentName component)99   public static AndroidComponentAddress forComponent(ComponentName component) {
100     return new AndroidComponentAddress(
101         new Intent(ApiConstants.ACTION_BIND).setComponent(component));
102   }
103 
104   /**
105    * Returns the Authority which is the package name of the target app.
106    *
107    * <p>See {@link android.content.ComponentName}.
108    */
getAuthority()109   public String getAuthority() {
110     return getComponent().getPackageName();
111   }
112 
getComponent()113   public ComponentName getComponent() {
114     return bindIntent.getComponent();
115   }
116 
117   /**
118    * Returns this address as an explicit {@link Intent} suitable for passing to {@link
119    * Context#bindService}.
120    */
asBindIntent()121   public Intent asBindIntent() {
122     return bindIntent.cloneFilter(); // Intent is mutable so return a copy.
123   }
124 
125   /**
126    * Returns this address as an "android-app://" uri.
127    *
128    * <p>See {@link Intent#URI_ANDROID_APP_SCHEME} for details.
129    */
asAndroidAppUri()130   public String asAndroidAppUri() {
131     Intent intentForUri = bindIntent;
132     if (intentForUri.getPackage() == null) {
133       // URI_ANDROID_APP_SCHEME requires an "explicit package name" which isn't set by any of our
134       // factory methods. Oddly, our explicit ComponentName is not enough.
135       intentForUri = intentForUri.cloneFilter().setPackage(getComponent().getPackageName());
136     }
137     return intentForUri.toUri(URI_ANDROID_APP_SCHEME);
138   }
139 
140   @Override
hashCode()141   public int hashCode() {
142     Intent intentForHashCode = bindIntent;
143     // Clear a (usually redundant) package filter to work around an Android >= 31 bug where certain
144     // Intents compare filterEquals() but have different filterHashCode() values. It's always safe
145     // to include fewer fields in the hashCode() computation.
146     if (intentForHashCode.getPackage() != null) {
147       intentForHashCode = intentForHashCode.cloneFilter().setPackage(null);
148     }
149     return intentForHashCode.filterHashCode();
150   }
151 
152   @Override
equals(Object obj)153   public boolean equals(Object obj) {
154     if (obj instanceof AndroidComponentAddress) {
155       AndroidComponentAddress that = (AndroidComponentAddress) obj;
156       return bindIntent.filterEquals(that.bindIntent);
157     }
158     return false;
159   }
160 
161   @Override
toString()162   public String toString() {
163     return "AndroidComponentAddress[" + bindIntent + "]";
164   }
165 }
166