1717package io .grpc .binder .internal ;
1818
1919import static com .google .common .base .Preconditions .checkState ;
20+ import static io .grpc .binder .internal .SystemApis .createContextAsUser ;
2021
2122import android .app .admin .DevicePolicyManager ;
2223import android .content .ComponentName ;
3233import androidx .annotation .AnyThread ;
3334import androidx .annotation .MainThread ;
3435import com .google .common .annotations .VisibleForTesting ;
35- import com .google .common .base .VerifyException ;
3636import com .google .errorprone .annotations .concurrent .GuardedBy ;
3737import io .grpc .Status ;
3838import io .grpc .StatusException ;
3939import io .grpc .binder .BinderChannelCredentials ;
40- import java .lang .reflect .Method ;
41- import java .util .List ;
4240import java .util .concurrent .Executor ;
4341import java .util .logging .Level ;
4442import java .util .logging .Logger ;
@@ -93,8 +91,6 @@ public String methodName() {
9391 private final Observer observer ;
9492 private final Executor mainThreadExecutor ;
9593
96- private static volatile Method queryIntentServicesAsUserMethod ;
97-
9894 @ GuardedBy ("this" )
9995 private State state ;
10096
@@ -194,8 +190,10 @@ private static Status bindInternal(
194190 break ;
195191 case BIND_SERVICE_AS_USER :
196192 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
193+ // We don't need SystemApis because bindServiceAsUser() is simply public in R+.
197194 bindResult = context .bindServiceAsUser (bindIntent , conn , flags , targetUserHandle );
198195 } else {
196+ // TODO(#12279): Use SystemApis to make this work pre-R.
199197 return Status .INTERNAL .withDescription ("Cross user Channel requires Android R+" );
200198 }
201199 break ;
@@ -266,51 +264,9 @@ void unbindInternal(Status reason) {
266264 }
267265 }
268266
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-
309267 @ AnyThread
310268 @ Override
311269 public ServiceInfo resolve () throws StatusException {
312- checkState (sourceContext != null );
313- PackageManager packageManager = sourceContext .getPackageManager ();
314270 int flags = 0 ;
315271 if (Build .VERSION .SDK_INT >= 29 ) {
316272 // Filter out non-'directBootAware' <service>s when 'targetUserHandle' is locked. Here's why:
@@ -320,9 +276,9 @@ public ServiceInfo resolve() throws StatusException {
320276 flags |= PackageManager .MATCH_DIRECT_BOOT_AUTO ;
321277 }
322278 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 );
326282 if (resolveInfo == null ) {
327283 throw Status .UNIMPLEMENTED // Same status code as when bindService() returns false.
328284 .withDescription ("resolveService(" + bindIntent + " / " + targetUserHandle + ") was null" )
@@ -331,6 +287,19 @@ public ServiceInfo resolve() throws StatusException {
331287 return resolveInfo .serviceInfo ;
332288 }
333289
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+
334303 @ MainThread
335304 private void clearReferences () {
336305 sourceContext = null ;
0 commit comments