title: "error code 5: database is locked" when using a ContentProvider
body: <p>I have an application that runs an activity with a service in a separate process that is started and bound to the activity.  The service contains a handler that posts a runnable to be run after a delay.</p>
  
  <p>I want each component to log to the database, so I implemented a content provider that deals with database access and I call it from the service or activity via extended AsyncTask sub-classes.</p>
  
  <p>This all works beautifully on the emulator, but when I run it in debug on my phone I get a sporadic database locked error on my database writing: </p>
  
  <p><strong>UPDATE</strong></p>
  
  <p>I made some changes to my database handling and the error has changed slightly.</p>
  
  <pre><code>ERROR/Database(15235): Error inserting MY_MESSAGE
  ERROR/Database(15235): android.database.sqlite.SQLiteException: error code 5: database is locked
  ERROR/Database(15235):     at android.database.sqlite.SQLiteStatement.native_execute(Native Method)
  ERROR/Database(15235):     at android.database.sqlite.SQLiteStatement.execute(SQLiteStatement.java:61)
  ERROR/Database(15235):     at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1591)
  ERROR/Database(15235):     at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1435)
  ERROR/Database(15235):     at mypackagename.DatabaseHelper.insertLogging(DatabaseHelper.java:190)
  ERROR/Database(15235):     at mypackagename.ContentProvider.insert(ContentProvider.java:139)
  ERROR/Database(15235):     at android.content.ContentProvider$Transport.insert(ContentProvider.java:198)
  ERROR/Database(15235):     at android.content.ContentResolver.insert(ContentResolver.java:604)
  ERROR/Database(15235):     at mypackagename.Activity$LogToDatabase.doInBackground(Activity.java:642)
  ERROR/Database(15235):     at mypackagename.Activity$LogToDatabase.doInBackground(Activity.java:1)
  ERROR/Database(15235):     at android.os.AsyncTask$2.call(AsyncTask.java:185)
  ERROR/Database(15235):     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
  ERROR/Database(15235):     at java.util.concurrent.FutureTask.run(FutureTask.java:138)
  ERROR/Database(15235):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
  ERROR/Database(15235):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
  ERROR/Database(15235):     at java.lang.Thread.run(Thread.java:1019)
  </code></pre>
  
  <p>I didn't put much detail before in because I thought it was an issue with different processes or threads, but now I'm thinking the problem is more likely located in the code calling the database.  </p>
  
  <p>Questions:</p>
  
  <p>1) Why am I hitting locks when I'm using a ContentProvider?<br>
  2) Why does this not show up on an equivalent API 2.3.3 emulator?<br>
  3) Does the fact that none of my code catches an exception mean that the error was handled properly and I can ignore it?<br>
  4) I read in another place someone suggesting adjusting the busy timeout.  How would I do that?  </p>
  
  <p>The irony that it's my debug logging that's causing the error is not lost on me.</p>
  
  <p>If I can't solve it, my next step is to bundle the logging messages up in a list and dump them out in batches of ten at a time.</p>
  
  <p>Here's the path the through the code to the error:</p>
  
  <p>Activity:</p>
  
  <pre><code>private void logDatabaseMessage(String status, String message)
  {
      String[] args = {status, message};
      LogToDatabase logTask = new LogToDatabase();
      logTask.execute(args);      
  }
  
  private class LogToDatabase extends AsyncTask&lt;String, Integer, Void&gt;
  {
      @Override
      protected Void doInBackground(final String... args) 
      {
          try
          {
              SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); 
              String dateText = dateFormat.format(new Date());
  
              ContentValues loggingValues = new ContentValues();
              loggingValues.put(MyContentProvider.LOGGING_DATETIME, dateText);
              loggingValues.put(MyContentProvider.LOGGING_STATUS, args[0]);
              loggingValues.put(MyContentProvider.LOGGING_MESSAGE, args[1]);
              getContentResolver().insert(MyContentProvider.LOGGING_CONTENT_URI, loggingValues);
          }
          catch (Exception ex)
          {
              Log.e(TAG, "LogToDatabase.doInBackground threw exception: " + ex.getMessage());
              ex.printStackTrace();
          }               
  
          return null;
      }
  }
  </code></pre>
  
  <p>ContentProvider:</p>
  
  <pre><code>@Override
  public Uri insert(Uri uri, ContentValues values) 
  {
      Uri _uri = null;
      long rowID = 0;
  
      try
      {
          switch (uriMatcher.match(uri))
          {
              case LOGGING:
                  rowID = dbHelper.insertLogging(values);
                  if (rowID == 0)
                      throw new SQLException("Failed to insert row into " + uri);
  
                  _uri = ContentUris.withAppendedId(LOGGING_CONTENT_URI, rowID);
                  break;
  
              default: throw new SQLException("Failed to insert row into " + uri);
          }
  
          if (rowID != 0)
              getContext().getContentResolver().notifyChange(_uri, null);    
      }
      catch (Exception ex)
      {
          Log.e(TAG, LogPrefix + "insert threw exception: " + ex.getMessage());
          ex.printStackTrace();
      }
  
      return _uri;    
  }
  </code></pre>
  
  <p>DatabaseHelper:</p>
  
  <pre><code>public long insertLogging(ContentValues values)
  {
      long rowID = 0;
  
      try
      {
          rowID = db.insert(LOGGING_TABLE, null,  values);
      }
      catch (Exception ex)
      {
          Log.e(TAG, LogPrefix + "ERROR: Failed to insert into logging table: " + ex.getMessage());
          ex.printStackTrace();
      }
  
      return rowID;
  }
  </code></pre>