Работа с неявным намерением будущего устаревания в Lollipop

Чтобы передавать данные в другие приложения, я использовал неявные намерения, как в примерах ниже:

Intent intent = new Intent();
intent.setAction("com.example.OpenURL");
intent.putExtra("URL_TO_OPEN", url_string);
sendOrderedBroadcastAsUser(intent);

Intent intent = new Intent();
intent.setAction("com.example.CreateUser");
intent.putExtra("Username", uname_string);
intent.putExtra("Password", pw_string);
sendBroadcast(intent);

Intent intent = new Intent();
intent.setAction("com.example.BackupUserData");
intent.setData(file_uri);
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
sendBroadcast(intent);

Но это поведение больше не рекомендуется в Android 5.0

http://developer.android.com/about/versions/android-5.0-changes.html

привязка к сервису

Теперь метод Context.bindService() требует явного намерения и бросает     исключение, если дано неявное намерение. Чтобы ваше приложение было безопасным, используйте     явное намерение при запуске или привязке вашего Сервиса и не объявлять намерения     фильтры для этой услуги.

С исходным кодом Android лучше обозначить класс ContextImpl:

private void validateServiceIntent(Intent service) {
        if (service.getComponent() == null && service.getPackage() == null) {
            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                IllegalArgumentException ex = new IllegalArgumentException(
                        "Service Intent must be explicit: " + service);
                throw ex;
            } else {
                Log.w(TAG, "Implicit intents with startService are not safe: " + service
                        + " " + Debug.getCallers(2, 3));
            }
        }
    }

Как я могу справиться с этим?

Ответ 1

Да, при запуске на устройстве с Android 5.0 этот код будет либо показывать предупреждение (если ваше приложение targetSdkVersion равно 21), либо сбой сразу (если таргетинг на Lollipop сам). Проверьте исходный код для validateServiceIntent() в ContextImpl.java:

Этот код опасен, так как он может позволить злоумышленнику зарегистрироваться и перехватить (или изменить) эти вызовы.

Наиболее разумной альтернативой является, вероятно, указание имени пакета приложения, которое вы хотите вызвать, с помощью Intent.setPackage(). Когда это делается, намерение уже не подразумевается, и оно будет работать. Например:

   intent.setPackage("com.example.app");

Конечно, если получатель находится внутри вашего приложения, есть более простые альтернативы (но это, похоже, не так, из вашего описания проблемы).

Ответ 2

Как сказано в других комментариях и ответах, это изменение относится только к bindService, а не к sendBroadcast, поэтому, если этот случай (как в вашем примере кода) - вам ничего не нужно менять.

Если вы используете bindService, способ сделать неявное намерение для явного намерения - использовать ComponentName и установить его в сервисе Intent с помощью setComponent или setClass.

Из документации класса Intent:

Разрешение на выполнение

Существуют две основные формы намерений, которые вы будете использовать.

Явные намерения указали компонент (через setComponent (ComponentName) или setClass (Контекст, класс)), который обеспечивает точный класс для запуска. Часто они не включают никаких другая информация, просто способ запуска приложения различные внутренние действия, которые он имеет, когда пользователь взаимодействует с выражение. Неявные намерения не указали компонент; вместо, они должны содержать достаточную информацию для системы, чтобы определить, какие из доступных компонентов лучше всего запускать для этого намерения.

Ответ 3

Как @Commonsware указал в blog, 3 способа решить это:

1. resolveService()

Intent i = new Intent("serviceName");
ResolveInfo info = ctx.getPackageManager().resolveService(i, Context.BIND_AUTO_CREATE);
i.setComponent(new ComponentName(info.serviceInfo.packageName,info.serviceInfo.name));

И используйте намерение привязать службу.

2. queryIntentServices()

Intent i = new Intent("serviceName");

List<ResolveInfo> infos = ctx.getPackageManager().queryIntentServices(i,Context.BIND_AUTO_CREATE);

if (infos.isEmpty()) {
   throw new IllegalStateException("no service found");
}
if (infos.size() > 1) {
   throw new SecurityException("multiple services found, could be a security issue");
}

i.setComponent(new ComponentName(infos.get(0).serviceInfo.packageName, infos.get(0).serviceInfo.name));

Если запрос возвращает более одной информации, это может означать прослушивание злоумышленных услуг.

3. setPackage()

Если у вас есть имя пакета, вы можете просто установить имя пакета как @matiash в своем сообщении:

Intent i = new Intent(MyClass.class.getName());
i.setPackage(MyClass.class.getPackage().getName())