Android Permission denial in Widget RemoteViewsFactory for Content - android

I have a widget which I am trying to use to display information from my app's local database inside of a listview.
I'm using the RemoteViewsService.RemoteViewsFactory interface to load my list's contents. If I run the block of code which reloads the list in the onDataSetChanged method. the app crashes with the following message:
11-01 16:40:39.540: E/ACRA(27175): DataDisplay fatal error : Permission Denial: reading com.datadisplay.content.ContentProviderAdapter uri content://com.datadisplay.provider.internalDB/events from pid=573, uid=10029 requires the provider be exported, or grantUriPermission()
However, this same code run in the class's constructor works just fine. Of course, I need to have this also work in the onDataSetChanged method for updating and stuff.
Here is my provider's entry in the manifest:
<provider android:name="com.datadisplay.content.ContentProviderAdapter"
android:authorities="com.datadisplay.provider.internalDB"
android:exported="true"
android:enabled="true"
android:grantUriPermissions="true">
<grant-uri-permission android:pathPattern="/events/"/>
</provider>
I am both exporting it AND granting Uri permissions like the error message requests, but it still fails. I found this question, where the guy had an issue but eventually removes his custom permissions and it worked. I don't have any custom permissions like that, but still no luck:
Widget with content provider; impossible to use ReadPermission?
If anyone has insight I'd be really grateful, this is getting incredibly frustrating, haha.

This is happening because RemoteViewsFactory is being called from a remote process, and that context is being used for permission enforcement. (The remote caller doesn't have permission to use your provider, so it throws a SecurityException.)
To solve this, you can clear the identity of the remote process, so that permission enforcement is checked against your app instead of against the remote caller. Here's a common pattern you'll find across the platform:
final long token = Binder.clearCallingIdentity();
try {
[perform your query, etc]
} finally {
Binder.restoreCallingIdentity(token);
}

Put this in your onDataSetChanged() method:
Thread thread = new Thread() {
public void run() {
query();
}
};
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
}
Fetch data from the database inside query() method. I do not know why fetching data in a separate thread helps get around this problem, but it works! I got this from one of the Android examples.

If this only happens for 4.2 and not the rest, you need to set the android:exported="true", because the default is changed:
http://developer.android.com/about/versions/android-4.2.html
Content providers are no longer exported by default. That is, the default value for the android:exported attribute is now “false". If it’s important that other apps be able to access your content provider, you must now explicitly set android:exported="true".

Related

Ask to be the app for spam detection on a android device with Flutter

I need to publish my flutter application on the playstore but I received several rejections. My application is used to identify the caller using my database.
I think my problem is that I don't know how to ask permission to become the default application for spam detection. Does anyone have the answer to this?
I've tried to change the permissions i asked in the android manifest, my last version is this :
\<uses-permission android:name="android.permission.READ_PHONE_STATE"/\> \<uses-permission android:name="android.permission.READ_CALL_LOG"/\> \<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS"/\>
Thank you very much for your help !
In order to screen calls correctly should extend the CallScreeningService class:
class MyCallScreeningService : CallScreeningService() {
override fun onScreenCall(details: Call.Details) {
val callResponse = when {
// Perform checks to determine if the call should be blocked or allowed.
// Return a new CallResponse object with the appropriate response action.
// e.g. CallResponse.reject() to block the call, CallResponse.allow() to allow the call.
else -> null // Return null if the call should be allowed.
}
respondToCall(details, callResponse) // Send the call response to the system.
}
}
You'll still need to register your CallScreeningService implementation in your AndroidManifest.xml file for it to be used by the system. Also, you will need to request the necessary permissions to access call details and control call responses.
Here's a more detailed article about this subject

Catching an exception globally and changing activity

I have an app that consumes an API. With every request, I send a header with the version of my app, the server checks it, and if the version is too low, it would throw an error back. I did it like this so that if in the future I introduce breaking changes then I can show a nice message on the app instead of just crashing
Now any request throughout the app can potentially return this error, so what I want to do is catch this globally on every request and close whichever activity is open and open a new one saying some nice message like "Please go to play store to update your app".
Is it possible to do such thing?
To recap, i want to do 2 things:
From any request, generate an ObsoleteAppException through OkHttp/Retrofit
Capture only this exception globally, close any activity and open a new one with the warning
If its any useful, I am using RxJava2 and retrofit
Use Thread.setDefaultUncaughtExceptionHandler in your Application class to catch all exceptions in your app and check if the app should crash or show an update message.
This article Hide your crashes gracefully (and still report them) and many other libraries can help you "Catch exception globally in Android".
As for changing current activity, there are already answers here. It basiclly tracks every activity. When you need to change current activity, just finish current one and start a new one.
This whole approach seems like a bit of overkill for your case but I don't have a better idea...Maybe a custom RxJava exception handler to catch this exception?
You can use UCE Handler to catch exceptions and present the user with options on how to handle it.
In your Project's build.gradle file:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
In your Application's or Module's build.gradle file:
dependencies {
compile 'com.github.jampez77:UCE-Handler:uce_handler:1.4.1'
}
In your Application class initialize library using builder pattern:
public class MyApplication extends Application {
#Override
public void onCreate() {
...
// Initialize UCE_Handler Library
new UCEHandler.Builder(this).build();
}
}
Then make sure you update you manifest as follows:
<application
android:name=".MyApplication"

Mobile backend starter continuous query never returns

I've written an Android client for a mobile backend starter app according to this tutorial. Everything works up to the section implementing Continuous Queries.
I've written a query and I'm calling it from the correct place in the code (onPostCreate()), however the query never returns any data.
I don't believe this is an authentication problem because I'm able to make other calls successfully.
Here is the code which never returns a result:
CloudCallbackHandler<List<CloudEntity>> handler = new CloudCallbackHandler<List<CloudEntity>>() {
#Override
public void onComplete(List<CloudEntity> results) {
for (CloudEntity entity : results) {
UserLocation loc = new UserLocation(entity);
mUserLocations.remove(loc);
mUserLocations.add(loc);
drawMarkers();
}
}
#Override
public void onError(IOException e) {
Toast.makeText(getApplicationContext(), e.getMessage(),
Toast.LENGTH_LONG).show();
}
};
CloudQuery query = new CloudQuery("UserLocation");
query.setLimit(50);
query.setSort(CloudEntity.PROP_UPDATED_AT, Order.DESC);
query.setScope(Scope.FUTURE_AND_PAST);
getCloudBackend().list(query, handler);
With the debugger I've verified that the getCloudBackend().list() line executes, but the onComplete() method is never hit, and neither is onError().
Here is an example of a call that works perfectly:
UserLocation self = new UserLocation(super.getAccountName(),
gh.encode(mCurrentLocation));
getCloudBackend().update(self.asEntity(), updateHandler);
Essentially, getCloudBackend().update() works, while getCloudBackend().list() does not.
I should also add that I've downloaded the full source from the github repo linked in the tutorial, and the same problem exists with that code.
I've also tried re-deploying the backend server multiple times.
Ok so I have finally fixed the problem! The issue is both in the manifest and in the class GCMIntentService.java
In the manifest the GCM is registered as a service and belongs to a package. By default this service is a part of the default package com.google.cloud.backend.android. When you create a new package and have all your client code in there, you need to move the GCMIntentService.java class into that new package and in the manifest modify the service and broadcast receiver
<service android:name="yourpackagename.GCMIntentService" />
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="yourpackagename" />
</intent-filter>
</receiver>
Any other permission that comes with the default package name should also be updated to the main package name. This doesn't need to be modified if you're only going to use that one default package that comes with the mobile backend starter.
Regarding the GoogleAuthIOException I received that as well initially. So I redid all the steps to enable GCM and authentication. Things to keep in mind though are that I still followed the tutorial and went with Web Application -> Generic when registering the GCM server key and Web Client ID. Also another key thing to keep in mind when registering for the Android Client ID is that with your SHA1 fingerprint it also needs a package name. Again the package name has to be your main client package if you're using more than one package for your project. You can get the project number that goes in the Consts.java (and it's required to register GCM) from the old Google API console and the project ID from the new cloud console. The Web client ID also goes in the Consts.java file and also in that same file you have to enable auth by changing
public static final boolean IS_AUTH_ENABLED = false;
to
public static final boolean IS_AUTH_ENABLED = true;
Hope this helps.
So I am also getting the SAME EXACT problem you are. getCloudBackend().update() works for me, and not only with the geohasher class, I also tried to send updates to the cloud with myLocation.toString() where myLocation is a LatLng and it gets updated fine.
Sorry for not giving you the actual solution to your problem. It's a really odd situation that the same exact code worked in the Google I/O demo but not when we (and I followed the tutorial very thoroughly) actually try it out. I feel that this is a server problem if anything.
Thanks for reporting this -- sorry you are having a problem. THe most likely problem is in configuring GCM. Can you verify you have GCM enabled on the project and all the setup steps where done correctly? Maybe try to send a message and see if that works?

"unable to open the database file" error in Flex for Android

Using Flex 4.5 for Android development, this is the script that should create the database:
private var db:File = File.userDirectory.resolvePath("events.db");
private var conn:SQLConnection;
public function MyDB() {
conn = new SQLConnection();
conn.addEventListener(SQLEvent.OPEN, openHandler);
conn.addEventListener(SQLErrorEvent.ERROR, errorHandler);
conn.open(db, );
}
and I have added this permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
but I get this error:
SQLError: 'Error #3125: Unable to open the database file.', details:'Connection closed.', operation:'open', detailID:'1001'
at flash.data::SQLConnection/internalOpen()
at flash.data::SQLConnection/open()
at com.galleons.util::MyDB()[/Users/luca/Documents/Adobe Flash Builder 4.5/Galleons/src/com/galleons/util/MyDB.as:24]
I know it's an old question, but anyway I was facing the same error and found the cause. If any of the parent directories of File which you pass to SQLConnection.open() does not exist, Flash Player throws an Error with detailID=1001. Simply call dbFile.parent.createDirectory() and the error should be gone.
Similar answer was given on Adobe Forums: SQLError #3125
Have you checked the 'usual suspects'?
file exists
not locked by some other app / stale version of your app
path is correct
At least part of the problem is due to mixing the SQLConnection class's open() method – which is synchronous – with events that are only supposed to be used when opening an asynchronous connection. You would open an asynchronous connection by using the openAsync() method instead of the open() method.
The docs are contradictory in this matter because it is, in fact, possible to listen for SQLEvent.OPEN when opening a synchronous connection. However, notice that the SQLErrorEvent.ERROR listener is not being triggered in your code and you are instead getting a runtime error. The docs make no mention of SQLErrorEvent.ERROR working with a synchronous connection; that does appear to be the case.
It's possible this is an AIR bug, but I suspect mixing synchronous methods with asynchronous event listeners is just a gray area. It's also likely that the problem could be solved if you instead wrap the open() call in a try/catch block, which is the recommended way to catch synchronous errors:
try
{
conn.open(db);
trace("Hey, is that a database?", (db.exists));
}
catch (err:SQLError)
{
trace("Error, database not created:", err.message);
trace("Error details:", err.details);
}

Permission Denial when Sync is canceled

I have implemented SyncAdapter to perform sync of items in application. This adapter is invoked correctly by Android when sync is requested either programmatically or automatically.
When I try to cancel the sync operation manually by deselecting the check box under Accounts and sync setting >{myappccount} > Data and Synchronisation > {app item} , my sync adapters onSyncCanceled is also get called correctly.
But when I my app try to read some internal setting through content provider query , it receives "java.lang.SecurityException: Permission Denial". Although same query works well during normal execution of application or during sync .
Below is stack trace.
java.lang.SecurityException: Permission Denial: reading com.my.applications.sync.content.MySettingProvider uri content://com.my.applications.sync.provider.mysetting/currentstateid from pid=0, uid=1000 requires null
at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:307)
at android.content.ContentProvider$Transport.query(ContentProvider.java:186)
at android.content.ContentResolver.query(ContentResolver.java:262)
at com.my.applications.sync.service.MysyncService.getCurrentServiceUri(MysyncService.java:442)
at com.my.applications.sync.service.MysyncService.cancelSync(MysyncService.java:1723)
at com.my.applications.sync.syncadapter.OtherSyncAdapter.onSyncCanceled(OtherSyncAdapter.java:51)
at android.content.AbstractThreadedSyncAdapter$ISyncAdapterImpl.cancelSync(AbstractThreadedSyncAdapter.java:121)
at android.content.ISyncAdapter$Stub.onTransact(ISyncAdapter.java:78)
at android.os.Binder.execTransact(Binder.java:320)
at dalvik.system.NativeStart.run(Native Method)
Do I need to add any permission for my internal content provider?
It looks from your stack trace that you've declared a readPermission on your ContentProvider. When onSyncCanceled(..) is called on your app it's an RPC call from the sync manager (typically) - and the Sync manager is not likely to hold the readPermission.
Try the following in #onCancelSync(..) to run your cancel code without the callers pid/uid involved:
long caller = android.os.Binder.clearCallingIdentity();
try {
// Do your stuff here
} finally {
android.os.Binder.restoreCallingIdentity(caller);
}
Br,
Jens

Categories

Resources