How to make an implicit intent explicit?

2019-06-05 08:25发布

问题:

I have two applications, A and B, which use a android library C. B has a service A wants to use via C, e.g.

<service android:name="my.package.in.a.service.ConnectorService"
         android:exported="true">
    <intent-filter>
        <action android:name="my.package.in.a.action.START_A"/>
    </intent-filter>
</service>

In my library there is a class which tries to bind it to the service, e.g.

    Intent intent = new Intent("my.package.in.a.service.ConnectorService");
    /** establish a connection with the service. */
    boolean result = context.bindService(intent, messenger,
         Context.BIND_AUTO_CREATE);

Apparently, you can't do that anymore because of security issues (implicit vs. explicit intents). I've tried to initialise the intent using the action defined in A. I've also tried to add the package name and also tried to set the class name, e.g.

   Intent intent = new Intent()
   intent.setPackage("my.package.in.a.service");
   intent.setClassName("my.package.in.a.service",
            "my.package.in.a.service.ConnectorService");

I've als tried to find the service using the package manager, e.g.

   Intent intent = new Intent("my.package.in.a.service.ConnectorService");
   List<ResolveInfo> resolveInfoList = context.getPackageManager()
            .queryIntentServices(intent, Context.BIND_AUTO_CREATE);

    if (resolveInfoList.isEmpty()) {
        Log.e(TAG, "could not find any service");
    }

    if (resolveInfoList.size() > 1) {
        Log.e(TAG, "multiple services found");
    }

I'm a bit puzzled what I'm doing wrong? As far as I understood it you can make an implicit intent explicit, even while it is not part of the same package/application, by simply specifying the package/classname. However, all this seems to fail and obviously I'm doing something wrong?

回答1:

I don't think that setPackage() does "enough" to make it fully explicit. For that, you need setComponent().

Your second approach, using PackageManager, is on the right track, but unless you just truncated your code listing, you missed the adjust-the-Intent step.

In this sample app from this book, I not only check for a single implementation of the service, but I also check its signature (to ensure the app I want to bind to is not hacked) and adjust the Intent:

Intent implicit=new Intent(IDownload.class.getName());
List<ResolveInfo> matches=getActivity().getPackageManager()
  .queryIntentServices(implicit, 0);

if (matches.size() == 0) {
  Toast.makeText(getActivity(), "Cannot find a matching service!",
    Toast.LENGTH_LONG).show();
}
else if (matches.size() > 1) {
  Toast.makeText(getActivity(), "Found multiple matching services!",
    Toast.LENGTH_LONG).show();
}
else {
  ServiceInfo svcInfo=matches.get(0).serviceInfo;

  try {
    String otherHash=SignatureUtils.getSignatureHash(getActivity(),
      svcInfo.applicationInfo.packageName);
    String expected=getActivity().getString(R.string.expected_sig_hash);

    if (expected.equals(otherHash)) {
      Intent explicit=new Intent(implicit);
      ComponentName cn=new ComponentName(svcInfo.applicationInfo.packageName,
        svcInfo.name);

      explicit.setComponent(cn);
      appContext.bindService(explicit, this, Context.BIND_AUTO_CREATE);
    }
    else {
      Toast.makeText(getActivity(), "Unexpected signature found!",
        Toast.LENGTH_LONG).show();
    }
  }
  catch (Exception e) {
    Log.e(getClass().getSimpleName(), "Exception trying to get signature hash", e);
  }
}