17
17
package io .grpc .binder .internal ;
18
18
19
19
import static com .google .common .base .Preconditions .checkState ;
20
+ import static io .grpc .binder .internal .SystemApis .createContextAsUser ;
20
21
21
22
import android .app .admin .DevicePolicyManager ;
22
23
import android .content .ComponentName ;
32
33
import androidx .annotation .AnyThread ;
33
34
import androidx .annotation .MainThread ;
34
35
import com .google .common .annotations .VisibleForTesting ;
35
- import com .google .common .base .VerifyException ;
36
36
import com .google .errorprone .annotations .concurrent .GuardedBy ;
37
37
import io .grpc .Status ;
38
38
import io .grpc .StatusException ;
39
39
import io .grpc .binder .BinderChannelCredentials ;
40
- import java .lang .reflect .Method ;
41
- import java .util .List ;
42
40
import java .util .concurrent .Executor ;
43
41
import java .util .logging .Level ;
44
42
import java .util .logging .Logger ;
@@ -93,8 +91,6 @@ public String methodName() {
93
91
private final Observer observer ;
94
92
private final Executor mainThreadExecutor ;
95
93
96
- private static volatile Method queryIntentServicesAsUserMethod ;
97
-
98
94
@ GuardedBy ("this" )
99
95
private State state ;
100
96
@@ -194,8 +190,10 @@ private static Status bindInternal(
194
190
break ;
195
191
case BIND_SERVICE_AS_USER :
196
192
if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
193
+ // We don't need SystemApis because bindServiceAsUser() is simply public in R+.
197
194
bindResult = context .bindServiceAsUser (bindIntent , conn , flags , targetUserHandle );
198
195
} else {
196
+ // TODO(#12279): Use SystemApis to make this work pre-R.
199
197
return Status .INTERNAL .withDescription ("Cross user Channel requires Android R+" );
200
198
}
201
199
break ;
@@ -266,51 +264,9 @@ void unbindInternal(Status reason) {
266
264
}
267
265
}
268
266
269
- // Sadly the PackageManager#resolveServiceAsUser() API we need isn't part of the SDK or even a
270
- // @SystemApi as of this writing. Modern Android prevents even system apps from calling it, by any
271
- // means (https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces).
272
- // So instead we call queryIntentServicesAsUser(), which does more than we need but *is* a
273
- // @SystemApi in all the SDK versions where we support cross-user Channels.
274
- @ Nullable
275
- private static ResolveInfo resolveServiceAsUser (
276
- PackageManager packageManager , Intent intent , int flags , UserHandle targetUserHandle ) {
277
- List <ResolveInfo > results =
278
- queryIntentServicesAsUser (packageManager , intent , flags , targetUserHandle );
279
- // The first query result is "what would be returned by resolveService", per the javadoc.
280
- return (results != null && !results .isEmpty ()) ? results .get (0 ) : null ;
281
- }
282
-
283
- // The cross-user Channel feature requires the client to be a system app so we assume @SystemApi
284
- // queryIntentServicesAsUser() is visible to us at runtime. It would be visible at build time too,
285
- // if our host system app were written to call it directly. We only have to use reflection here
286
- // because grpc-java is a library built outside the Android source tree where the compiler can't
287
- // see the "non-SDK" @SystemApis that we need.
288
- @ Nullable
289
- @ SuppressWarnings ("unchecked" ) // Safe by PackageManager#queryIntentServicesAsUser spec in AOSP.
290
- private static List <ResolveInfo > queryIntentServicesAsUser (
291
- PackageManager packageManager , Intent intent , int flags , UserHandle targetUserHandle ) {
292
- try {
293
- if (queryIntentServicesAsUserMethod == null ) {
294
- synchronized (ServiceBinding .class ) {
295
- if (queryIntentServicesAsUserMethod == null ) {
296
- queryIntentServicesAsUserMethod =
297
- PackageManager .class .getMethod (
298
- "queryIntentServicesAsUser" , Intent .class , int .class , UserHandle .class );
299
- }
300
- }
301
- }
302
- return (List <ResolveInfo >)
303
- queryIntentServicesAsUserMethod .invoke (packageManager , intent , flags , targetUserHandle );
304
- } catch (ReflectiveOperationException e ) {
305
- throw new VerifyException (e );
306
- }
307
- }
308
-
309
267
@ AnyThread
310
268
@ Override
311
269
public ServiceInfo resolve () throws StatusException {
312
- checkState (sourceContext != null );
313
- PackageManager packageManager = sourceContext .getPackageManager ();
314
270
int flags = 0 ;
315
271
if (Build .VERSION .SDK_INT >= 29 ) {
316
272
// Filter out non-'directBootAware' <service>s when 'targetUserHandle' is locked. Here's why:
@@ -320,9 +276,9 @@ public ServiceInfo resolve() throws StatusException {
320
276
flags |= PackageManager .MATCH_DIRECT_BOOT_AUTO ;
321
277
}
322
278
ResolveInfo resolveInfo =
323
- targetUserHandle != null
324
- ? resolveServiceAsUser ( packageManager , bindIntent , flags , targetUserHandle )
325
- : packageManager .resolveService (bindIntent , flags );
279
+ getContextForTargetUser ( "Cross-user pre-auth" )
280
+ . getPackageManager ( )
281
+ .resolveService (bindIntent , flags );
326
282
if (resolveInfo == null ) {
327
283
throw Status .UNIMPLEMENTED // Same status code as when bindService() returns false.
328
284
.withDescription ("resolveService(" + bindIntent + " / " + targetUserHandle + ") was null" )
@@ -331,6 +287,19 @@ public ServiceInfo resolve() throws StatusException {
331
287
return resolveInfo .serviceInfo ;
332
288
}
333
289
290
+ private Context getContextForTargetUser (String purpose ) throws StatusException {
291
+ checkState (sourceContext != null , "Already unbound!" );
292
+ try {
293
+ return targetUserHandle == null
294
+ ? sourceContext
295
+ : createContextAsUser (sourceContext , targetUserHandle , /* flags= */ 0 );
296
+ } catch (ReflectiveOperationException e ) {
297
+ throw Status .INTERNAL
298
+ .withDescription (purpose + " requires SDK_INT >= R and @SystemApi visibility" )
299
+ .asException ();
300
+ }
301
+ }
302
+
334
303
@ MainThread
335
304
private void clearReferences () {
336
305
sourceContext = null ;
0 commit comments