简体   繁体   中英

android content provider robustness on provider crash

On android platforms (confirmed on ICS), if a content provider dies while a client is in the middle of a query (ie has a open cursor) the framework decides to kill the client processes holding a open cursor.

Here is a logcat output when i tried this with a download manager query that sleeps after doing a query. The "sleep" was to reproduce the problem. you can imagine it happening in a regular use case when the provider dies at the right/wrong time. And then do a kill of com.android.media (which hosts the downloadProvider).

"Killing com.example (pid 12234) because provider com.android.providers.downloads.DownloadProvider is in dying process android.process.media"

I tracked the code for this in ActivityManagerService::removeDyingProviderLocked

 10203     private final void removeDyingProviderLocked(ProcessRecord proc,
 10204             ContentProviderRecord cpr) {
 10205         synchronized (cpr) {
 10206             cpr.launchingApp = null;
 10207             cpr.notifyAll();
 10208         }
 10210         mProvidersByClass.remove(cpr.name);
 10211         String names[] = cpr.info.authority.split(";");
 10212         for (int j = 0; j < names.length; j++) {
 10213             mProvidersByName.remove(names[j]);
 10214         }
 10215 
 10216         Iterator<ProcessRecord> cit = cpr.clients.iterator();
 10217         while (cit.hasNext()) {
 10218             ProcessRecord capp = cit.next();
 10219             if (!capp.persistent && capp.thread != null
 10220                     && capp.pid != 0
 10221                     && capp.pid != MY_PID) {
 10222                 Slog.i(TAG, "Kill " + capp.processName
 10223                         + " (pid " + capp.pid + "): provider " + cpr.info.name
 10224                         + " in dying process " + (proc != null ? proc.processName : "??"));
 10225                 EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
 10226                         capp.processName, capp.setAdj, "dying provider "
 10227                                 + cpr.name.toShortString());
 10228                 Process.killProcessQuiet(capp.pid);
 10229             }
 10230         }
 10231 
 10232         mLaunchingProviders.remove(cpr);
 10233     }

Is this a policy decision or is the cursor access unsafe after the provider has died?

It looks like the client cursor is holding a fd for an ashmem location populated by the CP. Is this the reason the clients are killed instead of throwing an exception like Binders when the server (provider) dies ?

The Cursor is unsafe. Although I think most of the time Cursor s are used only for "read access" to data, they are more complex than that.

The short explanation is in the Android docs that describes a Cursor :

This interface provides random read-write access to the result set returned by a database query.

So Cursor s are not just data held in an object. They hold your place in a ResultSet through a database connection. The ResultSet uses JDBC to access a database. So the Cursor is acting only as a "Java friendly" database call. Here are the Android for the ResultSet docs on that:

When reading data via the appropriate getter methods, the JDBC driver maps the SQL data retrieved from the database to the Java type implied by the method invoked by the application. The JDBC specification has a table for the mappings from SQL types to Java types.

The ResultSet keeps a connection to the database. The data is not "copied" into the interface (both Cursor and ResultSet are interfaces, not objects; some implementations may copy the data, but I haven't tested it because leaving Statement and ResultSet resources open through a closed Connection can cause DB resource issues).

Java provides the interface to gain JDBC access to a "results set table" in the database for the ResultSet as described in the Java docs here:

A table of data representing a database result set, which is usually generated by executing a statement that queries the database.

http://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html

You cannot provide "database access" to a ContentProvider that has died because there is no connection the ResultSet table in the database, so the Cursor should be disposed of.

EDIT:

One comment suggests that Android does not use JDBC' or ResultSet` - the Android implementation of the SQLite is a very similar implementation to JDBC, and the concepts are essentially the same without the convenient names and descriptions.

The fact that Android uses a custom implementation makes the description of the problem more difficult, although I should have made reference to this in my original post. Here is the Google Groups thread on the status of JDBC and the SQLite interface implementation in Android, if more detailed references and information is required:

https://groups.google.com/forum/#!topic/android-developers/zz3qlNL2JDw

From the discussion, you could probably ask Joerg Pleumann for more details...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM