diff options
6 files changed, 197 insertions, 65 deletions
diff --git a/OpenPGP-Keychain-API-Demo/res/layout/crypto_provider_demo.xml b/OpenPGP-Keychain-API-Demo/res/layout/crypto_provider_demo.xml index ecc2b4ac5..e4c9e1441 100644 --- a/OpenPGP-Keychain-API-Demo/res/layout/crypto_provider_demo.xml +++ b/OpenPGP-Keychain-API-Demo/res/layout/crypto_provider_demo.xml @@ -8,12 +8,37 @@ android:layout_height="match_parent" android:orientation="vertical" > - <Button - android:id="@+id/crypto_provider_demo_register" + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Encrypt User Id" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <EditText + android:id="@+id/crypto_provider_demo_encrypt_user_id" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="dominik@dominikschuermann.de" + android:textAppearance="@android:style/TextAppearance.Small" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Sign User Id" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <EditText + android:id="@+id/crypto_provider_demo_sign_user_id" android:layout_width="match_parent" android:layout_height="wrap_content" - android:onClick="registerCryptoProvider" - android:text="Register crypto provider" /> + android:text="dominik@dominikschuermann.de" + android:textAppearance="@android:style/TextAppearance.Small" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Message" + android:textAppearance="?android:attr/textAppearanceMedium" /> <EditText android:id="@+id/crypto_provider_demo_message" @@ -22,25 +47,17 @@ android:text="message" android:textAppearance="@android:style/TextAppearance.Small" /> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Ciphertext" + android:textAppearance="?android:attr/textAppearanceMedium" /> + <EditText android:id="@+id/crypto_provider_demo_ciphertext" android:layout_width="match_parent" android:layout_height="150dip" - android:text="-----BEGIN PGP MESSAGE----- -Charset: UTF-8 -Version: GnuPG v1.4.12 (GNU/Linux) -Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ - -hQEMAwCgOoTtKmfpAQgAlzmyQYCfkalCsAIxwtroHV9Bwz5LWY4GJNVrFBVddSFJ -VkVxx/UDqtIS+TvL8RBG3Er3xFilTV+iBSDjSGIXHkSv6Z7Od69nKcQpJLNaCpDj -/Ag7PsINLUzAvUdPto3ZMCwShe/uoD4e4Gr5BG8na/9W77tegufS2gUUlc5BAOZP -GUlSPySJP2bpI/3U/R86Z2ByzFKeJIEKWBtBvMPmIgA5VPo0+mamTedRhOIrJM/R -vUMM2HfLjAxcX9lYEw4aQGROOu1xpN9FPojQOO10imibZb+TEcxtSHwpj2vll1BP -pXvtuR0E9OGVmRI9aBXiRTB2P9SJ6UPpR13m8FaLVtJPAa2xH4wA5Yr6uZ5x7LjO -BtO8VErKgoUpO57BgU1ZsVFEcrGiobkreXabKIB+qC0qMJ6maoLlnOPi1IAvhU42 -z/7HBqqhcNiHc5JMs9+wmw== -=00nh ------END PGP MESSAGE-----" + android:text="ciphertext" android:textAppearance="@android:style/TextAppearance.Small" /> <Button @@ -51,6 +68,20 @@ z/7HBqqhcNiHc5JMs9+wmw== android:text="Encrypt" /> <Button + android:id="@+id/crypto_provider_demo_sign" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:onClick="signOnClick" + android:text="Sign" /> + + <Button + android:id="@+id/crypto_provider_demo_encrypt_and_sign" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:onClick="encryptAndSignOnClick" + android:text="Encrypt and Sign" /> + + <Button android:id="@+id/crypto_provider_demo_decrypt" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoService.aidl b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoService.aidl index 04c8eb30e..53f39dffc 100644 --- a/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoService.aidl +++ b/OpenPGP-Keychain-API-Demo/src/org/openintents/crypto/ICryptoService.aidl @@ -37,30 +37,30 @@ interface ICryptoService { oneway void encrypt(in byte[] inputBytes, in String[] encryptionUserIds, in ICryptoCallback callback); /** - * Encrypt and sign + * Sign * * @param inputBytes * Byte array you want to encrypt - * @param encryptionUserIds - * User Ids (emails) of recipients * @param signatureUserId * User Ids (email) of sender * @param callback * Callback where to return results */ - oneway void encryptAndSign(in byte[] inputBytes, in String[] encryptionUserIds, String signatureUserId, in ICryptoCallback callback); + oneway void sign(in byte[] inputBytes, String signatureUserId, in ICryptoCallback callback); /** - * Sign + * Encrypt and sign * * @param inputBytes * Byte array you want to encrypt + * @param encryptionUserIds + * User Ids (emails) of recipients * @param signatureUserId * User Ids (email) of sender * @param callback * Callback where to return results */ - oneway void sign(in byte[] inputBytes, String signatureUserId, in ICryptoCallback callback); + oneway void encryptAndSign(in byte[] inputBytes, in String[] encryptionUserIds, String signatureUserId, in ICryptoCallback callback); /** * Decrypts and verifies given input bytes. If no signature is present this method diff --git a/OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/CryptoProviderDemoActivity.java b/OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/CryptoProviderDemoActivity.java index 4522110f0..831c269e1 100644 --- a/OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/CryptoProviderDemoActivity.java +++ b/OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/CryptoProviderDemoActivity.java @@ -39,15 +39,17 @@ import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.EditText; import android.widget.ListAdapter; import android.widget.TextView; public class CryptoProviderDemoActivity extends Activity { Activity mActivity; - TextView mMessageTextView; - TextView mCiphertextTextView; - TextView mDataTextView; + EditText mMessage; + EditText mCiphertext; + EditText mEncryptUserId; + EditText mSignUserId; private CryptoServiceConnection mCryptoServiceConnection; @@ -58,9 +60,10 @@ public class CryptoProviderDemoActivity extends Activity { mActivity = this; - mMessageTextView = (TextView) findViewById(R.id.crypto_provider_demo_message); - mCiphertextTextView = (TextView) findViewById(R.id.crypto_provider_demo_ciphertext); - mDataTextView = (TextView) findViewById(R.id.aidl_demo_data); + mMessage = (EditText) findViewById(R.id.crypto_provider_demo_message); + mCiphertext = (EditText) findViewById(R.id.crypto_provider_demo_ciphertext); + mEncryptUserId = (EditText) findViewById(R.id.crypto_provider_demo_encrypt_user_id); + mSignUserId = (EditText) findViewById(R.id.crypto_provider_demo_sign_user_id); selectCryptoProvider(); } @@ -72,7 +75,9 @@ public class CryptoProviderDemoActivity extends Activity { @Override public void onEncryptSignSuccess(byte[] outputBytes) throws RemoteException { - // not needed here + Log.d(Constants.TAG, "onEncryptSignSuccess"); + + // TODO } @Override @@ -80,32 +85,53 @@ public class CryptoProviderDemoActivity extends Activity { throws RemoteException { Log.d(Constants.TAG, "onDecryptVerifySuccess"); - // PgpData data = new PgpData(); - // data.setDecryptedData(new String(outputBytes)); - // mFragment.setMessageWithPgpData(data); + mMessage.setText(new String(outputBytes)); } @Override public void onError(CryptoError error) throws RemoteException { Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId()); - Log.e(Constants.TAG, "onError getErrorId:" + error.getMessage()); + Log.e(Constants.TAG, "onError getMessage:" + error.getMessage()); } }; public void encryptOnClick(View view) { - byte[] inputBytes = mMessageTextView.getText().toString().getBytes(); + byte[] inputBytes = mMessage.getText().toString().getBytes(); try { mCryptoServiceConnection.getService().encrypt(inputBytes, - new String[] { "dominik@dominikschuermann.de" }, callback); + new String[] { mEncryptUserId.getText().toString() }, callback); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoProviderDemo", e); + } + } + + public void signOnClick(View view) { + byte[] inputBytes = mMessage.getText().toString().getBytes(); + + try { + mCryptoServiceConnection.getService().sign(inputBytes, + mSignUserId.getText().toString(), callback); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoProviderDemo", e); + } + } + + public void encryptAndSignOnClick(View view) { + byte[] inputBytes = mMessage.getText().toString().getBytes(); + + try { + mCryptoServiceConnection.getService().encryptAndSign(inputBytes, + new String[] { mEncryptUserId.getText().toString() }, + mSignUserId.getText().toString(), callback); } catch (RemoteException e) { Log.e(Constants.TAG, "CryptoProviderDemo", e); } } public void decryptOnClick(View view) { - byte[] inputBytes = mCiphertextTextView.getText().toString().getBytes(); + byte[] inputBytes = mCiphertext.getText().toString().getBytes(); try { mCryptoServiceConnection.getService().decryptAndVerify(inputBytes, callback); diff --git a/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoService.aidl b/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoService.aidl index 04c8eb30e..53f39dffc 100644 --- a/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoService.aidl +++ b/OpenPGP-Keychain/src/org/openintents/crypto/ICryptoService.aidl @@ -37,30 +37,30 @@ interface ICryptoService { oneway void encrypt(in byte[] inputBytes, in String[] encryptionUserIds, in ICryptoCallback callback); /** - * Encrypt and sign + * Sign * * @param inputBytes * Byte array you want to encrypt - * @param encryptionUserIds - * User Ids (emails) of recipients * @param signatureUserId * User Ids (email) of sender * @param callback * Callback where to return results */ - oneway void encryptAndSign(in byte[] inputBytes, in String[] encryptionUserIds, String signatureUserId, in ICryptoCallback callback); + oneway void sign(in byte[] inputBytes, String signatureUserId, in ICryptoCallback callback); /** - * Sign + * Encrypt and sign * * @param inputBytes * Byte array you want to encrypt + * @param encryptionUserIds + * User Ids (emails) of recipients * @param signatureUserId * User Ids (email) of sender * @param callback * Callback where to return results */ - oneway void sign(in byte[] inputBytes, String signatureUserId, in ICryptoCallback callback); + oneway void encryptAndSign(in byte[] inputBytes, in String[] encryptionUserIds, String signatureUserId, in ICryptoCallback callback); /** * Decrypts and verifies given input bytes. If no signature is present this method diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoService.java index a70641b58..7ff8c0e3e 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoService.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/crypto_provider/CryptoService.java @@ -56,8 +56,6 @@ public class CryptoService extends Service { PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, mPoolQueue); - private ArrayList<String> mAllowedPackages; - public static final String ACTION_SERVICE_ACTIVITY = "org.sufficientlysecure.keychain.crypto_provider.IServiceActivityCallback"; @Override @@ -65,10 +63,6 @@ public class CryptoService extends Service { super.onCreate(); mContext = this; Log.d(Constants.TAG, "CryptoService, onCreate()"); - - // load allowed packages from database - mAllowedPackages = ProviderHelper.getCryptoConsumers(mContext); - Log.d(Constants.TAG, "allowed: " + mAllowedPackages); } @Override @@ -95,6 +89,40 @@ public class CryptoService extends Service { } } + private synchronized void encryptSafe(byte[] inputBytes, String[] encryptionUserIds, + ICryptoCallback callback) throws RemoteException { + try { + // build InputData and write into OutputStream + InputStream inputStream = new ByteArrayInputStream(inputBytes); + long inputLength = inputBytes.length; + InputData inputData = new InputData(inputStream, inputLength); + + OutputStream outStream = new ByteArrayOutputStream(); + + // TODO: hardcoded... + boolean useAsciiArmor = true; + int compressionId = 2; // zlib + + // PgpMain.encryptAndSign(this, this, inputData, outStream, useAsciiArmor, + // compressionId, encryptionKeyIds, encryptionPassphrase, Preferences + // .getPreferences(this).getDefaultEncryptionAlgorithm(), + // secretKeyId, + // Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences + // .getPreferences(this).getForceV3Signatures(), + // PassphraseCacheService.getCachedPassphrase(this, secretKeyId)); + + outStream.close(); + } catch (Exception e) { + Log.e(Constants.TAG, "KeychainService, Exception!", e); + + try { + callback.onError(new CryptoError(0, e.getMessage())); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + private synchronized void decryptAndVerifySafe(byte[] inputBytes, ICryptoCallback callback) throws RemoteException { try { @@ -171,10 +199,22 @@ public class CryptoService extends Service { private final ICryptoService.Stub mBinder = new ICryptoService.Stub() { @Override - public void encrypt(byte[] inputBytes, String[] encryptionUserIds, ICryptoCallback callback) - throws RemoteException { - // TODO Auto-generated method stub + public void encrypt(final byte[] inputBytes, final String[] encryptionUserIds, + final ICryptoCallback callback) throws RemoteException { + Runnable r = new Runnable() { + + @Override + public void run() { + try { + encryptSafe(inputBytes, encryptionUserIds, callback); + } catch (RemoteException e) { + Log.e(Constants.TAG, "CryptoService", e); + } + } + }; + + checkAndEnqueue(r); } @Override @@ -218,9 +258,6 @@ public class CryptoService extends Service { public void register(boolean success, String packageName) throws RemoteException { if (success) { - // reload allowed packages - mAllowedPackages = ProviderHelper.getCryptoConsumers(mContext); - // resume threads if (isPackageAllowed(packageName)) { mThreadPool.resume(); @@ -287,8 +324,11 @@ public class CryptoService extends Service { private boolean isPackageAllowed(String packageName) { Log.d(Constants.TAG, "packageName: " + packageName); + ArrayList<String> allowedPkgs = ProviderHelper.getCryptoConsumers(mContext); + Log.d(Constants.TAG, "allowed: " + allowedPkgs); + // check if package is allowed to use our service - if (mAllowedPackages.contains(packageName)) { + if (allowedPkgs.contains(packageName)) { Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName); return true; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 70be38e21..a34cda0c3 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,8 +17,6 @@ package org.sufficientlysecure.keychain.provider; -import java.io.File; -import java.io.FileNotFoundException; import java.util.Arrays; import java.util.HashMap; @@ -44,7 +42,6 @@ import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; -import android.os.ParcelFileDescriptor; import android.provider.BaseColumns; import android.text.TextUtils; @@ -82,6 +79,7 @@ public class KeychainProvider extends ContentProvider { private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; private static final int CRYPTO_CONSUMERS = 301; + private static final int CRYPTO_CONSUMERS_BY_ROW_ID = 302; // private static final int DATA_STREAM = 401; @@ -230,6 +228,8 @@ public class KeychainProvider extends ContentProvider { * Crypto Consumers */ matcher.addURI(authority, KeychainContract.BASE_CRYPTO_CONSUMERS, CRYPTO_CONSUMERS); + matcher.addURI(authority, KeychainContract.BASE_CRYPTO_CONSUMERS + "/#", + CRYPTO_CONSUMERS_BY_ROW_ID); /** * data stream @@ -293,6 +293,9 @@ public class KeychainProvider extends ContentProvider { case CRYPTO_CONSUMERS: return CryptoConsumers.CONTENT_TYPE; + case CRYPTO_CONSUMERS_BY_ROW_ID: + return CryptoConsumers.CONTENT_ITEM_TYPE; + default: throw new UnsupportedOperationException("Unknown uri: " + uri); } @@ -653,6 +656,7 @@ public class KeychainProvider extends ContentProvider { rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case PUBLIC_KEY_RING_KEY: @@ -660,11 +664,13 @@ public class KeychainProvider extends ContentProvider { rowId = db.insertOrThrow(Tables.KEYS, null, values); rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case PUBLIC_KEY_RING_USER_ID: rowId = db.insertOrThrow(Tables.USER_IDS, null, values); rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case SECRET_KEY_RING: @@ -672,6 +678,7 @@ public class KeychainProvider extends ContentProvider { rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case SECRET_KEY_RING_KEY: @@ -679,6 +686,7 @@ public class KeychainProvider extends ContentProvider { rowId = db.insertOrThrow(Tables.KEYS, null, values); rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case SECRET_KEY_RING_USER_ID: @@ -697,7 +705,6 @@ public class KeychainProvider extends ContentProvider { // notify of changes in db getContext().getContentResolver().notifyChange(uri, null); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); } catch (SQLiteConstraintException e) { Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?"); @@ -725,6 +732,7 @@ public class KeychainProvider extends ContentProvider { count = db.delete(Tables.KEY_RINGS, buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), selectionArgs); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: case SECRET_KEY_RING_BY_MASTER_KEY_ID: @@ -733,24 +741,29 @@ public class KeychainProvider extends ContentProvider { count = db.delete(Tables.KEY_RINGS, buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), selectionArgs); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case SECRET_KEY_RING_KEY_BY_ROW_ID: count = db.delete(Tables.KEYS, buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: case SECRET_KEY_RING_USER_ID_BY_ROW_ID: count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection), selectionArgs); break; + case CRYPTO_CONSUMERS_BY_ROW_ID: + count = db.delete(Tables.CRYPTO_CONSUMERS, + buildDefaultCryptoConsumersSelection(uri, selection), selectionArgs); + break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } // notify of changes in db getContext().getContentResolver().notifyChange(uri, null); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); return count; } @@ -776,6 +789,8 @@ public class KeychainProvider extends ContentProvider { values, buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), selectionArgs); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); + break; case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: case SECRET_KEY_RING_BY_MASTER_KEY_ID: @@ -786,6 +801,8 @@ public class KeychainProvider extends ContentProvider { values, buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), selectionArgs); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); + break; case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case SECRET_KEY_RING_KEY_BY_ROW_ID: @@ -793,6 +810,8 @@ public class KeychainProvider extends ContentProvider { .update(Tables.KEYS, values, buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs); + sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); + break; case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: case SECRET_KEY_RING_USER_ID_BY_ROW_ID: @@ -805,7 +824,6 @@ public class KeychainProvider extends ContentProvider { // notify of changes in db getContext().getContentResolver().notifyChange(uri, null); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); } catch (SQLiteConstraintException e) { Log.e(Constants.TAG, "Constraint exception on update! Entry already existing?"); @@ -888,6 +906,23 @@ public class KeychainProvider extends ContentProvider { return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andSelection; } + /** + * Build default selection statement for Crypto Consumers. If no extra selection is specified + * only build where clause with rowId + * + * @param uri + * @param selection + * @return + */ + private String buildDefaultCryptoConsumersSelection(Uri uri, String selection) { + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; + } + + return selection + andSelection; + } + // @Override // public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { // int match = mUriMatcher.match(uri); |