checking permissions in custom account authenticator - android

i have a custom authenticator, and i'd like to expose the user / password to other applications. to protect from any random app obtaining the credentials, i'd like to perform something like a permissions check in my custom authenticator's getAuthToken() method. what's the correct method?
i tried this,
int p = context.checkCallingPermission("com.whatever.AUTH");
if (p != PackageManager.PERMISSION_GRANTED) {
where "com.whatever.AUTH" is defined in the app hosting my authenticator,
<permission android:name="com.vmware.horizon.AUTH" />
however, in my test app that does not have a uses-permission in it's manifest, when i request the account,
AccountManagerFuture<Bundle> future = am.getAuthToken(new Account(
"com.whatever", "com.whatever"),
"com.whatever", new Bundle(), this,
new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> future) {
String token = result.getString(AccountManager.KEY_AUTHTOKEN);
}
}, handler);
i successfully obtain the auth token. debugging shows that the call through to my authenticator's getAuthToken() method happens, but the check permission call returned "granted".
EDIT: if i get the package name from the context i'm using to call checkCallingPermission() it is the package name of the app hosting the custom authenticator. if i get the calling PID, UID they are 0 and 1000, respectively.
any ideas?

When looking at the Android source code, I noticed that the account manager service sets the caller's pid and uid on the Bundle as AccountManager.KEY_CALLER_PID and AccountManager.KEY_CALLER_UID.
You can use getInt() on the bundle to find out the real caller's pid and uid in your getAppToken method.
One other useful bit of information that can be hard to find. The getAppToken method is often only called once since the account manager service caches the result. If you want to be able to manage the token more actively, you can disable caching by adding meta-data to your manifest entry:
<service android:name=".authenticator.AccountAuthenticatorService">
<meta-data android:name="android.accounts.AccountAuthenticator" android:resource="#xml/authenticator"/>
<meta-data android:name="android.accounts.AccountAuthenticator.customTokens" android:value="1"/>
</service>
Here is what the authenticator xml looks like:
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.your.account.type"
android:customTokens="true"
android:icon="#drawable/logo"
android:smallIcon="#drawable/logo"
android:label="#string/app_name_long"/>

Related

Can't get permission BIND_NOTIFICATION_LISTENER_SERVICE for NotificationListenerService

I created an app and added a java class that extend `NotificationListenerService'.
Everything should work fine, but I just can't get to permission BIND_NOTIFICATION_LISTENER_SERVICE.
I added it on the manifest, but when I check for the permission with:
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE)
!= PackageManager.PERMISSION_GRANTED)
{
Log.d(TAG, "permission denied");
}
else
Log.d(TAG, "permission granted");
but I keep getting permission denied. I know that this permission isn't a "dangerous permission" so I don't have to ask for it from the user.
In the manifest I declared this:
<service android:name=".PcNotification"
android:label="PCNotificationService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
and it still doesn't work.
I also implemented the function onListenerConnected but it never called, so it means that my service never gets connected to the notification manager, probably because I don't have the permission BIND_NOTIFICATION_LISTENER_SERVICE
I just want to know how to grant this permission to my app.
I just can't get to permission BIND_NOTIFICATION_LISTENER_SERVICE
You do not need to request that permission. You need to defend your service with that permission. You are doing that in the <service> element via the android:permission attribute.
I know that this permission isn't a "dangerous permission" so I don't have to ask for it from the user.
Then get rid of the code that is requesting it from the user. It is unnecessary, and it will not work.
This should be the literal answer to the original question.
Nevertheless, if you need the method below, you misunderstood the use of such services (as did I). You should not launch your notification listener yourself, you should simply test if your service is running, and if not, then you already know that the permission was not granted, and you can point the user to the preferences panel at android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS.
Add this method to your NotificationListenerClass, and it will return true if the permission is granted, false if not:
public Boolean VerifyNotificationPermission() {
String theList = android.provider.Settings.Secure.getString(getContentResolver(), "enabled_notification_listeners");
String[] theListList = theList.split(":");
String me = (new ComponentName(this, YourNotificationListenerClass.class)).flattenToString();
for ( String next : theListList ) {
if ( me.equals(next) ) return true;
}
return false;
}
The string "enabled_notification_listeners" seems to be defined under the hood in Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, but I cannot resolve that, so I use the perhaps not so well maintainable literal string. If anyone knows how to get it by its reference, please add it / edit!
This specific permission must be granted manually by the user in android Settings, after that your service will be executed as you bound the permission to your service in AndroidManifest.xml with this:
<service android:name=".PcNotification"
android:label="PCNotificationService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
The problem is that most users won't know where to grant this permission (e.g inside android Settings), so you can open it for them with this:
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
You can run this when the user interacts with your app - e.g clicking a button - and ideally you explain why you need this permission.
I find particularly nice to have a card with the explanation and a button to open the settings so the user can enable.

How to enforce policy from maas360 mdm to my android app

I am trying to enforce a policy on android app and my app is not able to understand that policy.
I have written a simple code where I am asking for a string from maas360 mdm.
Following is my Android manifest code snippet:
<receiver android:name=".GetRestrictionReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_RESTRICTIONS_CHANGED"></action>
</intent-filter>
</receiver>
And following is my broadcast receiver:
public void onReceive(Context context, Intent intent) {
Log.d("Get Restriction", "on receive");
RestrictionsManager restrictionsManager = (RestrictionsManager) context.getSystemService(Context.RESTRICTIONS_SERVICE);
Bundle b = restrictionsManager.getApplicationRestrictions();
if(b.containsKey("siteName")) {
Log.d("Get Restriction", "Site name= "+b.getString("siteName"));
}
//String value = intent.getStringExtra("siteName");
}
Following is my app_restriction xml:
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
<restriction
android:key="siteName"
android:title="SiteName"
android:restrictionType="string"
android:defaultValue="English">
</restriction>
</restrictions>
Unfortunately my broadcast is not receiving my policy from maas360 mdm.
Would you help me understand what am I missing from my code that will get me the policy?
If you check out this page:
https://developer.android.com/work/managed-configurations.html#listen-configuration
You'll see the following -
Note: The ACTION_APPLICATION_RESTRICTIONS_CHANGED intent is sent only to listeners that are dynamically registered, not to listeners that are declared in the app manifest.
I see that you are delaring the listener in the app manifest. It needs to be dynamically registered.

Adding Google Cloud Messagin (GCM) for Android - Registration process

I have been struggling with GCM implementation for some weeks now, but how I really want to get to understand how it works I decided to take 'baby steps'.
First of all, as mentioned here, I understood that the first thing to do is register my device/app must first register with GCM.
To verify that they can send and receive messages, client apps must register with GCM. In this process, the client obtains a unique registration token.
I'd like to know if the procedure and code below represent the very minimal code necessary to make such registration (the 'very minimal code necessary' is because when I learned OpenGL ES 2.0 and started to deal with shaders, I saw that when dealing with hard/confusing concepts, If you understand the minimal code necessary you can later understand what the "peripheral" code)
Procedure to registrate the app with GCM:
Create a project at Google Developer Console;
Activate "Cloud Messaging for Android" API;
Create a server API key at "Credentials";
Take note of the project ID;
Take note of the project number;
Take note of the server API key;
Create a Android Studio project;
Add an "App Engine Backend with CGM" module type;
On the "appengine-web.xml" window that will open, type in the application ID and the server API Key.
Create a main activity, using the project number (SENDER_ID).
After I didn all of that and run the app, I got a token with 152 characters.
Is all of that correct? Considering that I got a token back, is my device registered with GCM?
appengine-web.xml:
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>mygcmtest...</application>
<version>1</version>
<threadsafe>true</threadsafe>
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
<property name="gcm.api.key" value="AIza..." />
</system-properties>
</appengine-web-app>
MainActivity:
public class MainActivity extends AppCompatActivity {
private final Context mContext = this;
private final String SENDER_ID = "319899...";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getGCMToken();
}
private void getGCMToken() {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
InstanceID instanceID = InstanceID.getInstance(mContext);
String token = instanceID.getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Log.e("GCM Token", token);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}.execute();
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.aninha.mygcmtest..." >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission
android:name="com.example.gcm.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Is my answer with source code at your question How to implement a GCM Hello World for Android using Android Studio is not enough for you to create two simple GCM projects (server-side and client-side) yet? :)
Of course, the sample code I used is just for a very basic case "server app sends, client app receives and displays message".
To sum up, you need to do the following steps:
Create a new project at Google Developers Console . At this
step, for simplicity, you just need to take note of 2 values: Project Number, which
will be used as SENDER_ID in my client project; and API server key (created at Credentials), which
will be used as API_KEY in my server project.
Create a new simple Android project for server side (with basic source code as my answer in your previous question).
Create a new simple Android project for client side (with basic source code as my answer in your previous question, I customized from the original source at Google Cloud Messaging - GitHub).
Run the client app, you will get the registration token (means that your device has successfully registered). Then, paste (hard-code) this token at CLIENT_REGISTRATION_TOKEN variable in server app.
Run the server app, and check the result (client app received the message or not)
Hope this helps!
P/S: I don't use any appengine-web.xml file

Receive intent "android.settings.APPLICATION_DETAILS_SETTINGS" for my app

I want to get the package name and class name of the received intent, But am not able to get it.
I want to make my app secure so it asks for password before being uninstalled. Only the user who Installed the app knows the password, so only he/she can uninstall the app.
My code for Receiver:
public class PackageReceiver extends BroadcastReceiver {
# Override
public void onReceive (Context context, Intent intent) {
if (intent.getAction().equals("android.settings.APPLICATION_DETAILS_SETTINGS")) {
/ / TODO:
//I want here to get this getAction working and then I want to fetch package and class of the intent
}
}
}
Manifest:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RESTART_PACKAGES"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<Application
android: icon = "# drawable / ic_launcher"
android: label = "Test">
<Receiver android: name = ". PackageReceiver"
android: label = "# string / app_name">
<intent-filter>
<action android:name="android.settings.APPLICATION_DETAILS_SETTINGS" />
<data android:scheme="package" />
</ Intent-filter>
</ Receiver>
</ Application>
Please let me know if I am missing any permission because I can not get this working.
I personally think this behavior is annoying. And for sure it's redundant: there are other mechanisms already on place that tackle the same problem (screen locking, encryption).
I'd only use extra checks when operations are on your side (ie.: delete account, change email, etc).
If I didn't make it to discourage you to do that here's another post that solves the same problem following a similar direction.
When we select a particular app inside the settings screen, a broadcast of the type : android.intent.action.QUERY_PACKAGE_RESTART is fired with package name of the application as an extra. I think you could use that to fire the password dialog.
The receiver code will be something like this :
public void onReceive(Context context, Intent intent) {
String[] packageNames = intent.getStringArrayExtra("android.intent.extra.PACKAGES");
if(packageNames!=null){
for(String packageName: packageNames){
if(packageName!=null && packageName.equals("our_app_package_name")){
//Our app was selected inside settings. Fire Password Dialog.
}
}
}
}
I think intent.getExtra("com.android.settings.ApplicationPkgName") should have the package name
i dont know if its acceptable
setting package by getting packagename from the context of activity
intent.setPackage(context.getPackageName());
and to get package name
intent.getPackage()

android: validate the identity of intent sender

I work in a company that produces several apps, not all those apps have the same signature or more like it we have at least 5-6 apps certificates for the time being.
We tried to create a mechanism in which all the companie's apps on the same device share the same is,
For example if user installed from the market App A and no app installed, a new ID will be generated, if now he installs App A, app B should have the same id as App A(id is just a generated UUID type #4) etc...
We are using broadcast at the moment and only apps with our permission can receive that broadcast and send back the id with another broadcast(explicit this time).
The broadcast and the responses are protected with our permission with signature level, this is of course not helping since we have more than one signature.
I tried to write an intent broadcast and recover that can have it's own mechanism of protection that will not be limited to only one signature but several, the problem is that things like Binder.getSenderUID() doesn't work for broadcasts and i get my own uid.
it looks like i have no way to get the identity of my snder unless he itself writes his id in the intent, which is NOT something i can trust as it can be easily faked.
Using encryption requires the apps to come with a key on them, which is not secured once more, turning to a server for validation takes too much time and on mobile not guaranteed to success since not 100% sure there is network around.
Anyone has any idea how can one get a validate\secure message from one app to another ?(all my apps but may have different signatures).
As always with a challenging question here i never get a proper, if ANY!' answer, so i'm forced to find it myself.
The problem with Intents is that it's not possible to get the sender as they are the parallel to mulicast in a network where there sender's address is not important.
If i wish to get the snder's UID i need to make a "remote" process even if he is local, instead of using broadcast IPC i need to use AIDL with IBInder implementation.
Once i have a Binder object i can call in my service getCallingUid() and get the uid of the caller, this will allow me to ask PackageManager to give me it's public certificate (without asking the process itself, i ask the OS) and compare it to a set of certificates i prepared in advance in the apk.
The calling application on the other side(the other process that sends me it's ID) just has to use the bindService(service, conn, flags) method to bind to me.
The disadvantage to this approach is ofcourse the time consuming process, Bind takes time, it's an async call that pass through the kernel and is not as fast as binding to a local service. Moreover since i might have several applications i need to synchronize the access my internal ID so only the first binding call that didn't fail will set and ID for me.
I still need to check if i can use the Messanger method that prevents the multi thread issues.
Hopes this helps someone else.
Sorry for late response...
Bind takes time, and more importantly, its asynchronous.
However, there is a way to make a synchronous bind - assuming of course the service you attempt to contact is already started at the time.
Android allowed for this more for BroadcastReceivers (which are async in nature, and thus can't use normal bindService) a BroadcastReceiver has a "peekService" method.
If you want to use it without listening to a broadcast, you can by doing:
final IBinder[] b = new IBinder[1];
new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
b[0] = peekService(context, intent);
}
}.onReceiver(context, intent);
IMyInterface i = IMyInterface.Stub.asInterface(b[0);
note that you don't bind to the service, so make sure to peek at on each use.
As already stated, binding is probably the best solution to this. However, you could consider switching to an Activity rather than a BroadcastReceiver then you can use getCallingActivity(), assuming you launched with startActivityForResult().
Declare you Activity as follows to make it "silent" like a BroadcastReceiver:
<activity
android:name=".FauxReceiver"
android:theme="#android:style/Theme.NoDisplay"
android:excludeFromRecents="true"
android:noHistory="true"
>
<intent-filter>
...
</intent-filter>
</activity>
Inspiration: How to get the sender of an Intent?
I was looking for a way to verify the package name of the application that sent the intent received by my intent-filter. That activity in my app that handles the intent-filter requires that the intent sender includes their process id in an Intent Extras field. My receiving activity can then get the associated application package name from the ActivityManager.
Here is some example code I found while shifting through StackOverflow.
Constants needed for both Apps
public static final String EXTRA_APP_ID;
public static final String ACTION_VERIFY = "com.example.receivingapp.action.VERIFY";
Calling Activity
Intent verifyIntent = new Intent();
verifyIntent.setAction(Consts.ACTION_VERIFY);
verifyIntent.putExtra(EXTRA_APP_ID, android.os.Process.myPid());
// Verify that the intent will resolve to an activity
if (verifyIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(verifyIntent, Consts.REQUEST_VERIFY);
} else {
Log.d(TAG, "Application not found.");
}
Receiving App
Manifest
<activity
android:name="com.example.receivingapp.ReceivingActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="com.example.receivingapp.VERIFY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
ReceivingActivity
if (getIntent().hasExtra(OnyxCoreConsts.EXTRA_APP_ID)) {
string appName = null;
// Resolve intent
if (getIntent().getAction().equals(ACTION_VERIFY) {
int appPid = getIntent().getIntExtra(EXTRA_APP_ID, -1);
if (-1 != mAppPid) {
appName = Utils.getAppNameByPID(mContext, mAppPid);
}
if (null != appName && !"".equalsIgnoreCase(appName)) {
// Do something with the application package name
}
}
}
Utils class
public static String getAppNameByPID(Context context, int pid){
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == pid) {
return processInfo.processName;
}
}
return "";
}

Categories

Resources