Enforcing android permissions within the same app - android

Here is a scenario. My app has
BroadcastSenderActivity
SampleBroadcastReceiver
I am using the sendBroadcast(intent, permission) method to send out my broadcast because I would like only my SampleBroadcastReceiver to receive the broadcast message and prevent other receivers in my app from receiving the message.
Note: All receivers are registered for the same action but at the time of broadcast based on the system conditions the message will be broadcasted with varying permissions.
From the documentation I learnt that I need the following in the manifest :
Need to declare <permission android:name="com.example.MYPERMISSION"
android:label="my_permission"
android:protectionLevel="normal"></permission>
in the manifest
Have this stmt to request for the permission which is at the app level <uses-permission android:name="com.example.MYPERMISSION"/>
QUESTION -
Instead of app-level permission request how can I request permission only for a particular component in my app? eg. SampleBroadcastReceiver (see code below). Is it possible?*
What's the purpose of the <android-permission> tag which is available for all inidividual components? Can it serve my purpose ?
AndroidManifest
.........
<activity
android:name="com.example.activities.BroadcastSenderActivity"
android:label="BroadcastSenderActivity"
android:theme="#android:style/Theme.Light" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- ************************************** RECEIVER ******************************************* -->
<receiver android:name="com.example.broadcastTest.SampleBroadcastReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.example.ACTION_BROADCAST_SEND" />
</intent-filter>
</receiver>
<receiver android:name="com.example.broadcastTest.BroadcastReceiver_1"
android:exported="false">
<intent-filter>
<action android:name="com.example.ACTION_BROADCAST_SEND" />
</intent-filter>
</receiver>
<receiver android:name="com.example.broadcastTest.BroadcastReceiver_2"
android:exported="false">
<intent-filter>
<action android:name="com.example.ACTION_BROADCAST_SEND" />
</intent-filter>
</receiver>
public class BroadcastSenderActivity extends Activity {
public BroadcastSenderActivity() {
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(Logger.TAG, "BroadcastSenderActivity - onCreate");
setContentView(R.layout.broadcast_tester);
Button btn = (Button)findViewById(R.id.sendBroadcastBtn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent broadcastIntent = new Intent();
Log.d(Logger.TAG, "BroadcastSenderActivity - sending the broadcast");
broadcastIntent.setAction(com.example.ACTION_BROADCAST_SEND);
BroadcastSenderActivity.this.sendBroadcast(broadcastIntent, com.example.MYPERMISSION);

You can secure each separate component with a specific permission. This is the android:permission="..." attribute that can be applied to each component separately. This allows you to require different permissions, for example, to launch a specific Activity, start a specific Service or use a specific BroadcastReceiver. This attribute indicates that the application needs to hold the specified permission in order to use the specific component.
An application holds a set of permissions. These permissions are requested in the manifest by the <uses-permission> tags. The user is shown this list when the application is installed and the application is granted these permissions upon installation.
When you send a broadcast, usually all installed and/or registered BroadcastReceivers are able to see this broadcast. When you use the method sendBroadcast(intent, permission) the permission parameter is used to control which receivers are able to see the broadcast. However, this permission checking is also done at the application level. This means that if an application has been granted the specified permission, then all of that application's BroadcastReceivers will be able to see the broadcast.
Therefore, what you want to do is not possible using the Android permission mechanism. Because an application either has or does not have a specific permission, you cannot have fine-grained control over which components can be used by a specific application.
Hopefully this answers your question.
EDIT: Alternative suggestion:
Since this is all within a single application, you should be able to manage this yourself. Personally, I think using the Android permission framework for this is overkill. You can just add an appropriate extra to the broadcast Intent and use that for controlling access to the receivers. Or, you can define 2 different Intent actions, and use that to control access.

Related

android BOOT_COMPLETED not received if not activity intent-filter

I set up a simple app. I wan't to hide it from the drawer and I want to add a Boot Receiver to launch a service.
To hide the application, I read that I had to remove this from the manifest
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
But when I remove it, the boot receiver doesn't work anymore.
I added the permission under the manifest tag
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
and my receiver under application
<receiver android:name="com.example.receiver.BootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
And in the receiver code, there is just a Toast
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Received", Toast.LENGTH_SHORT).show();
}
}
Why couldn't I set the boot receiver AND the hidden app from drawer?
Thanks
Starting with Android 3.1 all applications, upon installation, are placed in a "stopped" state.(This is the same state that the application ends up in after the user force-stops the app from the Settings application.)
While in "stopped" state, the application will not run for any reason, except by a manual launch of an activity. (Meaning no BroadcastReceviers(ACTION_PACKAGE_INSTALLED, BOOT_COMPLETED etc.) will be invoked, regardless of the event for which they have registered, until the user runs the app manually.)
This is an anti-malware move by Google. Google has advocated that users should launch an activity from the launcher first, before that application can go do much. Preventing BOOT_COMPLETED from being delivered until the activity is launched is a logical consequence of the that argument.
More details about this:
http://developer.android.com/about/versions/android-3.1.html#launchcontrols
http://commonsware.com/blog/2011/07/05/boot-completed-regression.html
http://devmaze.wordpress.com/2011/12/05/activating-applications/

Purpose of Service Intent-Filter inside Manifest.xml

from android developers : "Components(service) advertise their capabilities — the kinds of intents they can respond to — through intent filters.
I just cant understand the purpose of intent filter inside service in the Manifest.xml, what is the capability here?
<service
android:name="com.x.y"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.x.y" />
</intent-filter>
</service>
and what's he difference if i remove the intent-filter?
<service
android:name="com.x.y"
</service>
thanks.
If you want to use a service to perform different actions, then declaring an intent filter will help your service match against different actions you want to perform.
The example will explain better.
Suppose you have following declaration in manifest file:
<service
android:name="MyService" >
<intent-filter>
<action android:name="com.x.y.DOWNLOAD_DATA" />
<action android:name="com.x.y.UPLOAD_DATA" />
</intent-filter>
</service>
Then in your IntentService you could filter for these actions like this:
public class MyService extends IntentService {
public MyService() {
super("MyService");
}
#Override
protected void onHandleIntent(Intent intent) {
if(intent.getAction().equals("com.x.y.DOWNLOAD_DATA"){
//download data here
}else if(intent.getAction().equals("com.x.y.UPLOAD_DATA"){
// upload data here
}
}
}
Basically, it allows you to use the same service for different actions, instead of creating two separate services for example.
However, having intent filters declared for a service is not regarded as a good practice, and this is what the docs had to say:
Caution: To ensure your app is secure, always use an explicit intent
when starting a Service and do not declare intent filters for your
services. Using an implicit intent to start a service is a security
hazard because you cannot be certain what service will respond to the
intent, and the user cannot see which service starts.
You can use intent filters for explicitly calling your service or get your service to be implicitly called where components from any application installed on the user's device can potentially start your service
If you plan on using your service only locally (other applications do not use it), then you don't need to (and should not) supply any intent filters
It is clearly specified in the documentation Declaring a service in the manifest

android - "Exported receiver does not require permission" on receivers meant to receive from system services

I have some receivers declared in my AndroidManifest :
<!-- no warning -->
<receiver
android:name=".receivers.TriggerMonitoringBootReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- no warning -->
<receiver
android:name=".receivers.ScanResultsReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.net.wifi.SCAN_RESULTS" />
</intent-filter>
</receiver>
<!-- warning : Exported receiver does not require permission-->
<receiver
android:name=".receivers.BatteryMonitoringReceiver"
android:enabled="false">
<intent-filter>
<action android:name="#string/intent_action_setup_alarm" />
<action android:name="#string/intent_action_cancel_alarm" />
<action android:name="#string/intent_action_monitor" />
</intent-filter>
</receiver>
The first one is meant to receive a BOOT_COMPLETED action. The second is meant to receive android.net.wifi.SCAN_RESULTS. The third one is meant to receive some actions I broadcast (intent_action_monitor) and some actions broadcasted by the AlarmManager (intent_action_setup_alarm etc).
Two questions:
Why don't I get the warning on all receivers?
What permissions do I need to set for receivers meant to receive from system services to correct the warning (I understand what it is about and I don't want anyone to use my receivers anyway) ? Will exported="false" do for boot receivers, wifi receivers, alarm receivers etc?
I thought of using a custom permission with android:protectionLevel="signatureOrSystem" but the docs advise against both this protection level and custom permissions. So how I should handle this warning ?
Links to the docs and/or some code will be much appreciated.
Why don't I get the warning on all receivers ?
Because the first two are clearly designed to be broadcast by Android. The last one is unknown, partly because you did not supply the string resource values, and possibly because they are your own unique action strings.
What permissions do I need to set for receivers meant to receive from system services to correct the warning
The correct solution is to delete the <intent-filter>. If you are broadcasting these Intents, or if you are wrapping an Intent in a getBroadcast() PendingIntent, you do not need action strings. Use the Intent constructor that takes the Java class object as the second parameter, and use that:
new Intent(this, BatteryMonitoringReceiver.class)
You are welcome to still attach an action string to that Intent, if you want, but you can dump the <intent-filter> (routing will be based on the supplied component, in this case the Java class).
Only use an <intent-filter> when you are expecting the OS or third-party apps to initiate the Intent themselves (executing a PendingIntent that you created does not count).
The warning "Exported receiver does not require permission" means, You have an intent-filter with some action (which means by default you have android:exported="true" set and it can now receive broadcasts from ANY broadcasters outside of your application) Since it can receive broadcasts from ANY broadcasters outside of your application, it warns you by saying "Hey, are you sure ANY broadcaster can invoke you? In my opinion, it is better if you allow only those broadcasters to invoke you that has the permission you have set for this receiver through android:permission"
You can remove this warning by adding android:exported="false" to the receiver tag
If you do want to export your receiver to other processes, you can add your own permission definition in your android-manifest file for avoiding this warning, like
<permission
android:name="com.yourpage.permission.YOUR_PERMISSION"
android:protectionLevel="normal" />
<uses-permission
android:name="com.yourpage.permission.YOUR_PERMISSION" />
<receiver <!-- warning : Exported receiver does not require permission-->
android:name=".receivers.BatteryMonitoringReceiver"
android:permission="com.yourpage.permission.YOUR_PERMISSION"
android:enabled="false" >
<intent-filter>
<action android:name="#string/intent_action_setup_alarm" />
<action android:name="#string/intent_action_cancel_alarm" />
<action android:name="#string/intent_action_monitor" />
</intent-filter>
</receiver>
for more information, you can refer to http://developer.android.com/training/articles/security-tips.html
If, like me, you are here because your app built with a previous SDK version stopped working with more recent versions and you would like to fix it with minimal change, just add
android:exported=false
to the receiver tag in the manifest file. The solution by CommonsWare is obviously the one to go with for the long term but this fixes the issue temporarily if you are using custom intents and don't mean to export them.
Going by Lubo's way, you would need to export this custom permission, which would prompt the user before installation. That means the descriptive text for the permission needs to be well written so you don't end up scaring the user into changing his mind about installing the app. Also, it would need to be translated into all your target languages.
To hide this warning, add tools:ignore="ExportedReceiver" to the receiver:
<receiver
android:name=".MyReceiverIndentedForOtherAppsWithoutPermissions"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.my.app.CUSTOM_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>

How to use permission with signature protection?

I want to learn how to create a main application and its associated key application. The problem is I don't know how to make key application broadcast mainapp.action.VALID into main app.
#MainApp
<permission
android:name="mainapp.permission.CHECK_RESULT"
android:protectionLevel="signature" />
<application>
<receiver
android:name=".ResponseReceiver"
android:permission="mainapp.permission.CHECK_RESULT" >
<intent-filter>
<action android:name="mainapp.action.CHECK_OK" />
</intent-filter>
</receiver>
#KeyApp
Intent i = new Intent();
i.setAction("mainapp.action.CHECK_OK");
context.sendBroadcast(i, "mainapp.permission.CHECK_RESULT");
The result is, intent is rejected: it requires mainapp.permission.CHECK_RESULT permission. But If I remove the permission, mainapp.receiver.ResponseReceiver can receive the intent and also can confirm that both app use the same signature.
What do I miss here?
As nandeesh indicates, you need the corresponding <uses-permission> element in your KeyApp, saying that KeyApp requests the mainapp.permission.CHECK_RESULT permission.
Also, AFAIK, your second parameter to sendBroadcast() will require MainApp to also have the <uses-permission> element for mainapp.permission.CHECK_RESULT. If that is not your intent (pun intended (nested pun intended (oh, no! infinite pun recursion!))), I would drop that second parameter on the sendBroadcast() call.

PACKAGE_ADDED BroadcastReceiver doesn't work

I have a broadcast receiver registered in Manifest:
<application ...>
<receiver android:name="com.some.pkg.NewAppReceiver" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
</intent-filter>
</receiver>
</appcication>
And the receiver:
public class NewAppReceiver extends BroadcastReceiver {
private static final String TAG = "NewAppReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Intent: " + intent.getAction());
}
}
And nothing is received when I install APK manually or from the Android Market. Why?
Did you run the app that contains this broadcastReceiver before installing the other apps?
Starting at some API version, broadcastReceivers will not work till you execute the app. Put an activity and execute it.
Also , don't forget to add the following into the broadcastReceiver:
<data android:scheme="package" />
EDIT: On Android 8 and above, if your app targets API 27 or more, it will work partially, so you have to register to those events in code and not in manifest. Here's a list of intents that are still safe to use in manifest: https://developer.android.com/guide/components/broadcast-exceptions.html .
The rest should be used in code. More info here
Since android.intent.action.PACKAGE_ADDED is a System Intent (note that your own app will not receive it at its installation), your BroadcastReceiver will receive messages from sources outside your app. Thus, check you did NOT put: android:exported="false"
You also may need to add:
<data android:scheme="package" />
So, your BroadcastReceiver in your AndroidManifest.xml should look like this:
<application ...>
<receiver android:name=".NewAppReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
</appcication>
If it still doesn't work, you may try to put an higher priority, such as: android:priority="1000"
Take a look at: http://developer.android.com/guide/topics/manifest/receiver-element.html
Registering receiver from manifest would not work from API 26(android 8). Because it had performance impact on older versions.
But we can register receiver from java code and receive updates of removed and added applications.
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED)
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED)
intentFilter.addDataScheme("package")
registerReceiver(YourBroadcastReceiver(), intentFilter)
Are you trying to receive the intent in the application you are installing? The documentation for ACTION_PACKAGE_ADDED says:
Note that the newly installed package does not receive this broadcast.
Another possibility is that this intent might not be delivered to components registered via the manifest but only manually (as described in an answer by Mark Murphy to Stack Overflow question Can't receive broadcasts for PACKAGE intents).
If you try to receive some other package it must be worked.
(As #Savvas noted) If you try to receive your own package's addition you can't receive it. Even if your broadcast receiver has action.PACKAGE_ADDED, receiver's onReceive method isn't triggered.
In this case your best bet is saving this data. By using sharedPreferences, add a key something like "appIsWorkedBefore", and on your launcher Activity's onCreate method set this variable as "true". And you can make your works with respect to this Boolean.
This intent action is no longer available for applications.
This is a protected intent that can only be sent by the system.
https://developer.android.com/reference/android/content/Intent#ACTION_PACKAGE_ADDED

Categories

Resources