[automerger] Disallow attaching files from our own EmailAttachmentProvider. am: 44a5db2dc0 am: ec59cd4c39 am: 7764908d8d am: 29f9af1b0a am: 1f74cbabba am: 9a7c825a34 am: 4c824af722 am: 425e9f9156 am: c94203d0c3 am: 5d2c780a76 am: 35ead32a64

Change-Id: I93cb8abf76f74c8a867a957c995f53b3f7a47ca3
diff --git a/proguard-test.flags b/proguard-test.flags
index 2eb8cfa..b735814 100644
--- a/proguard-test.flags
+++ b/proguard-test.flags
@@ -29,10 +29,6 @@
   public static boolean updateMessageFields(...);
 }
 
--keep class com.android.mail.utils.Utils {
-  public static android.text.Spanned insertStringWithStyle(...);
-}
-
 -keepclasseswithmembers class * {
   *** findProviderForDomain(...);
 }
diff --git a/res/values-az-rAZ-sw600dp/strings.xml b/res/values-az-sw600dp/strings.xml
similarity index 100%
rename from res/values-az-rAZ-sw600dp/strings.xml
rename to res/values-az-sw600dp/strings.xml
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az/strings.xml
similarity index 100%
rename from res/values-az-rAZ/strings.xml
rename to res/values-az/strings.xml
diff --git a/res/values-bn-rBD-sw600dp/strings.xml b/res/values-bn-sw600dp/strings.xml
similarity index 100%
rename from res/values-bn-rBD-sw600dp/strings.xml
rename to res/values-bn-sw600dp/strings.xml
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn/strings.xml
similarity index 100%
rename from res/values-bn-rBD/strings.xml
rename to res/values-bn/strings.xml
diff --git a/res/values-et-rEE-sw600dp/strings.xml b/res/values-et-sw600dp/strings.xml
similarity index 100%
rename from res/values-et-rEE-sw600dp/strings.xml
rename to res/values-et-sw600dp/strings.xml
diff --git a/res/values-et-rEE/strings.xml b/res/values-et/strings.xml
similarity index 100%
rename from res/values-et-rEE/strings.xml
rename to res/values-et/strings.xml
diff --git a/res/values-eu-rES-sw600dp/strings.xml b/res/values-eu-sw600dp/strings.xml
similarity index 100%
rename from res/values-eu-rES-sw600dp/strings.xml
rename to res/values-eu-sw600dp/strings.xml
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu/strings.xml
similarity index 100%
rename from res/values-eu-rES/strings.xml
rename to res/values-eu/strings.xml
diff --git a/res/values-gl-rES-sw600dp/strings.xml b/res/values-gl-sw600dp/strings.xml
similarity index 100%
rename from res/values-gl-rES-sw600dp/strings.xml
rename to res/values-gl-sw600dp/strings.xml
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl/strings.xml
similarity index 100%
rename from res/values-gl-rES/strings.xml
rename to res/values-gl/strings.xml
diff --git a/res/values-gu-rIN-sw600dp/strings.xml b/res/values-gu-sw600dp/strings.xml
similarity index 100%
rename from res/values-gu-rIN-sw600dp/strings.xml
rename to res/values-gu-sw600dp/strings.xml
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu/strings.xml
similarity index 100%
rename from res/values-gu-rIN/strings.xml
rename to res/values-gu/strings.xml
diff --git a/res/values-hy-rAM-sw600dp/strings.xml b/res/values-hy-sw600dp/strings.xml
similarity index 100%
rename from res/values-hy-rAM-sw600dp/strings.xml
rename to res/values-hy-sw600dp/strings.xml
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy/strings.xml
similarity index 100%
rename from res/values-hy-rAM/strings.xml
rename to res/values-hy/strings.xml
diff --git a/res/values-is-rIS-sw600dp/strings.xml b/res/values-is-sw600dp/strings.xml
similarity index 100%
rename from res/values-is-rIS-sw600dp/strings.xml
rename to res/values-is-sw600dp/strings.xml
diff --git a/res/values-is-rIS/strings.xml b/res/values-is/strings.xml
similarity index 100%
rename from res/values-is-rIS/strings.xml
rename to res/values-is/strings.xml
diff --git a/res/values-ka-rGE-sw600dp/strings.xml b/res/values-ka-sw600dp/strings.xml
similarity index 100%
rename from res/values-ka-rGE-sw600dp/strings.xml
rename to res/values-ka-sw600dp/strings.xml
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka/strings.xml
similarity index 100%
rename from res/values-ka-rGE/strings.xml
rename to res/values-ka/strings.xml
diff --git a/res/values-kk-rKZ-sw600dp/strings.xml b/res/values-kk-sw600dp/strings.xml
similarity index 100%
rename from res/values-kk-rKZ-sw600dp/strings.xml
rename to res/values-kk-sw600dp/strings.xml
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk/strings.xml
similarity index 100%
rename from res/values-kk-rKZ/strings.xml
rename to res/values-kk/strings.xml
diff --git a/res/values-km-rKH-sw600dp/strings.xml b/res/values-km-sw600dp/strings.xml
similarity index 100%
rename from res/values-km-rKH-sw600dp/strings.xml
rename to res/values-km-sw600dp/strings.xml
diff --git a/res/values-km-rKH/strings.xml b/res/values-km/strings.xml
similarity index 100%
rename from res/values-km-rKH/strings.xml
rename to res/values-km/strings.xml
diff --git a/res/values-kn-rIN-sw600dp/strings.xml b/res/values-kn-sw600dp/strings.xml
similarity index 100%
rename from res/values-kn-rIN-sw600dp/strings.xml
rename to res/values-kn-sw600dp/strings.xml
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn/strings.xml
similarity index 100%
rename from res/values-kn-rIN/strings.xml
rename to res/values-kn/strings.xml
diff --git a/res/values-ky-rKG-sw600dp/strings.xml b/res/values-ky-sw600dp/strings.xml
similarity index 100%
rename from res/values-ky-rKG-sw600dp/strings.xml
rename to res/values-ky-sw600dp/strings.xml
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky/strings.xml
similarity index 100%
rename from res/values-ky-rKG/strings.xml
rename to res/values-ky/strings.xml
diff --git a/res/values-lo-rLA-sw600dp/strings.xml b/res/values-lo-sw600dp/strings.xml
similarity index 100%
rename from res/values-lo-rLA-sw600dp/strings.xml
rename to res/values-lo-sw600dp/strings.xml
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo/strings.xml
similarity index 100%
rename from res/values-lo-rLA/strings.xml
rename to res/values-lo/strings.xml
diff --git a/res/values-mk-rMK-sw600dp/strings.xml b/res/values-mk-sw600dp/strings.xml
similarity index 100%
rename from res/values-mk-rMK-sw600dp/strings.xml
rename to res/values-mk-sw600dp/strings.xml
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk/strings.xml
similarity index 100%
rename from res/values-mk-rMK/strings.xml
rename to res/values-mk/strings.xml
diff --git a/res/values-ml-rIN-sw600dp/strings.xml b/res/values-ml-sw600dp/strings.xml
similarity index 100%
rename from res/values-ml-rIN-sw600dp/strings.xml
rename to res/values-ml-sw600dp/strings.xml
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml/strings.xml
similarity index 100%
rename from res/values-ml-rIN/strings.xml
rename to res/values-ml/strings.xml
diff --git a/res/values-mn-rMN-sw600dp/strings.xml b/res/values-mn-sw600dp/strings.xml
similarity index 100%
rename from res/values-mn-rMN-sw600dp/strings.xml
rename to res/values-mn-sw600dp/strings.xml
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn/strings.xml
similarity index 100%
rename from res/values-mn-rMN/strings.xml
rename to res/values-mn/strings.xml
diff --git a/res/values-mr-rIN-sw600dp/strings.xml b/res/values-mr-sw600dp/strings.xml
similarity index 100%
rename from res/values-mr-rIN-sw600dp/strings.xml
rename to res/values-mr-sw600dp/strings.xml
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr/strings.xml
similarity index 100%
rename from res/values-mr-rIN/strings.xml
rename to res/values-mr/strings.xml
diff --git a/res/values-ms-rMY-sw600dp/strings.xml b/res/values-ms-sw600dp/strings.xml
similarity index 100%
rename from res/values-ms-rMY-sw600dp/strings.xml
rename to res/values-ms-sw600dp/strings.xml
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms/strings.xml
similarity index 100%
rename from res/values-ms-rMY/strings.xml
rename to res/values-ms/strings.xml
diff --git a/res/values-my-rMM-sw600dp/strings.xml b/res/values-my-sw600dp/strings.xml
similarity index 100%
rename from res/values-my-rMM-sw600dp/strings.xml
rename to res/values-my-sw600dp/strings.xml
diff --git a/res/values-my-rMM/strings.xml b/res/values-my/strings.xml
similarity index 100%
rename from res/values-my-rMM/strings.xml
rename to res/values-my/strings.xml
diff --git a/res/values-ne-rNP-sw600dp/strings.xml b/res/values-ne-sw600dp/strings.xml
similarity index 100%
rename from res/values-ne-rNP-sw600dp/strings.xml
rename to res/values-ne-sw600dp/strings.xml
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne/strings.xml
similarity index 100%
rename from res/values-ne-rNP/strings.xml
rename to res/values-ne/strings.xml
diff --git a/res/values-si-rLK-sw600dp/strings.xml b/res/values-si-sw600dp/strings.xml
similarity index 100%
rename from res/values-si-rLK-sw600dp/strings.xml
rename to res/values-si-sw600dp/strings.xml
diff --git a/res/values-si-rLK/strings.xml b/res/values-si/strings.xml
similarity index 100%
rename from res/values-si-rLK/strings.xml
rename to res/values-si/strings.xml
diff --git a/res/values-ta-rIN-sw600dp/strings.xml b/res/values-ta-sw600dp/strings.xml
similarity index 100%
rename from res/values-ta-rIN-sw600dp/strings.xml
rename to res/values-ta-sw600dp/strings.xml
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta/strings.xml
similarity index 100%
rename from res/values-ta-rIN/strings.xml
rename to res/values-ta/strings.xml
diff --git a/res/values-te-rIN-sw600dp/strings.xml b/res/values-te-sw600dp/strings.xml
similarity index 100%
rename from res/values-te-rIN-sw600dp/strings.xml
rename to res/values-te-sw600dp/strings.xml
diff --git a/res/values-te-rIN/strings.xml b/res/values-te/strings.xml
similarity index 100%
rename from res/values-te-rIN/strings.xml
rename to res/values-te/strings.xml
diff --git a/res/values-ur-rPK-sw600dp/strings.xml b/res/values-ur-sw600dp/strings.xml
similarity index 100%
rename from res/values-ur-rPK-sw600dp/strings.xml
rename to res/values-ur-sw600dp/strings.xml
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur/strings.xml
similarity index 100%
rename from res/values-ur-rPK/strings.xml
rename to res/values-ur/strings.xml
diff --git a/res/values-uz-rUZ-sw600dp/strings.xml b/res/values-uz-sw600dp/strings.xml
similarity index 100%
rename from res/values-uz-rUZ-sw600dp/strings.xml
rename to res/values-uz-sw600dp/strings.xml
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz/strings.xml
similarity index 100%
rename from res/values-uz-rUZ/strings.xml
rename to res/values-uz/strings.xml
diff --git a/src/com/android/mail/browse/EmlMessageViewFragment.java b/src/com/android/mail/browse/EmlMessageViewFragment.java
index 306c138..2fbbb72 100644
--- a/src/com/android/mail/browse/EmlMessageViewFragment.java
+++ b/src/com/android/mail/browse/EmlMessageViewFragment.java
@@ -27,7 +27,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.provider.OpenableColumns;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -337,7 +337,7 @@
                 return;
             }
 
-            ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(
+            ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(
                     data.getString(data.getColumnIndex(OpenableColumns.DISPLAY_NAME)));
         }
 
diff --git a/src/com/android/mail/compose/AttachmentComposeView.java b/src/com/android/mail/compose/AttachmentComposeView.java
index 89216e1..6f099c5 100644
--- a/src/com/android/mail/compose/AttachmentComposeView.java
+++ b/src/com/android/mail/compose/AttachmentComposeView.java
@@ -69,7 +69,8 @@
     private void populateAttachmentData(Context context) {
         ((TextView) findViewById(R.id.attachment_name)).setText(mAttachment.getName());
 
-        if (mAttachment.size > 0) {
+        // If size is -1, then it's size is unknown, hence do not show it.
+        if (mAttachment.size != -1) {
             ((TextView) findViewById(R.id.attachment_size)).
                     setText(AttachmentUtils.convertToHumanReadableSize(context, mAttachment.size));
         } else {
diff --git a/src/com/android/mail/compose/AttachmentsView.java b/src/com/android/mail/compose/AttachmentsView.java
index 5edac8a..285a437 100644
--- a/src/com/android/mail/compose/AttachmentsView.java
+++ b/src/com/android/mail/compose/AttachmentsView.java
@@ -15,18 +15,21 @@
  */
 package com.android.mail.compose;
 
+import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract;
 import android.provider.OpenableColumns;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
+import android.webkit.MimeTypeMap;
 import android.widget.LinearLayout;
 
 import com.android.mail.R;
@@ -37,6 +40,7 @@
 import com.android.mail.ui.AttachmentTileGrid;
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 
@@ -206,6 +210,39 @@
     }
 
     /**
+     * Checks if the passed Uri is a virtual document.
+     *
+     * @param contentUri
+     * @return true if virtual, false if regular.
+     */
+    @TargetApi(24)
+    private boolean isVirtualDocument(Uri contentUri) {
+        // For SAF files, check if it's a virtual document.
+        if (!DocumentsContract.isDocumentUri(getContext(), contentUri)) {
+          return false;
+        }
+
+        final ContentResolver contentResolver = getContext().getContentResolver();
+        final Cursor metadataCursor = contentResolver.query(contentUri,
+                new String[] { DocumentsContract.Document.COLUMN_FLAGS }, null, null, null);
+        if (metadataCursor != null) {
+            try {
+                int flags = 0;
+                if (metadataCursor.moveToNext()) {
+                    flags = metadataCursor.getInt(0);
+                }
+                if ((flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0) {
+                    return true;
+                }
+            } finally {
+                metadataCursor.close();
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * Generate an {@link Attachment} object for a given local content URI. Attempts to populate
      * the {@link Attachment#name}, {@link Attachment#size}, and {@link Attachment#contentType}
      * fields using a {@link ContentResolver}.
@@ -226,13 +263,26 @@
         if (contentType == null) contentType = "";
 
         final Attachment attachment = new Attachment();
-        attachment.uri = null; // URI will be assigned by the provider upon send/save
-        attachment.setName(null);
-        attachment.size = 0;
+        attachment.uri = null;  // URI will be assigned by the provider upon send/save
         attachment.contentUri = contentUri;
         attachment.thumbnailUri = contentUri;
 
         Cursor metadataCursor = null;
+        String name = null;
+        int size = -1;  // Unknown, will be determined either now or in the service
+        final boolean isVirtual = Utils.isRunningNOrLater()
+                ? isVirtualDocument(contentUri) : false;
+
+        if (isVirtual) {
+            final String[] mimeTypes = contentResolver.getStreamTypes(contentUri, "*/*");
+            if (mimeTypes != null && mimeTypes.length > 0) {
+                attachment.virtualMimeType = mimeTypes[0];
+            } else{
+                throw new AttachmentFailureException(
+                        "Cannot attach a virtual document without any streamable format.");
+            }
+        }
+
         try {
             metadataCursor = contentResolver.query(
                     contentUri, new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE},
@@ -240,8 +290,12 @@
             if (metadataCursor != null) {
                 try {
                     if (metadataCursor.moveToNext()) {
-                        attachment.setName(metadataCursor.getString(0));
-                        attachment.size = metadataCursor.getInt(1);
+                        name =  metadataCursor.getString(0);
+                        // For virtual document this size is not the one which will be attached,
+                        // so ignore it.
+                        if (!isVirtual) {
+                            size = metadataCursor.getInt(1);
+                        }
                     }
                 } finally {
                     metadataCursor.close();
@@ -259,38 +313,55 @@
                 metadataCursor = getOptionalColumn(contentResolver, contentUri,
                         OpenableColumns.DISPLAY_NAME);
                 if (metadataCursor != null && metadataCursor.moveToNext()) {
-                    attachment.setName(metadataCursor.getString(0));
+                    name = metadataCursor.getString(0);
                 }
             } finally {
                 if (metadataCursor != null) metadataCursor.close();
             }
 
             // Let's try to get SIZE
-            try {
-                metadataCursor =
-                        getOptionalColumn(contentResolver, contentUri, OpenableColumns.SIZE);
-                if (metadataCursor != null && metadataCursor.moveToNext()) {
-                    attachment.size = metadataCursor.getInt(0);
-                } else {
-                    // Unable to get the size from the metadata cursor. Open the file and seek.
-                    attachment.size = getSizeFromFile(contentUri, contentResolver);
+            if (!isVirtual) {
+                try {
+                    metadataCursor =
+                            getOptionalColumn(contentResolver, contentUri, OpenableColumns.SIZE);
+                    if (metadataCursor != null && metadataCursor.moveToNext()) {
+                        size = metadataCursor.getInt(0);
+                    } else {
+                        // Unable to get the size from the metadata cursor. Open the file and seek.
+                        size = getSizeFromFile(contentUri, contentResolver);
+                    }
+                } finally {
+                    if (metadataCursor != null) metadataCursor.close();
                 }
-            } finally {
-                if (metadataCursor != null) metadataCursor.close();
             }
         } catch (SecurityException e) {
             throw new AttachmentFailureException("Security Exception from attachment uri", e);
         }
 
-        if (attachment.getName() == null) {
-            attachment.setName(contentUri.getLastPathSegment());
-        }
-        if (attachment.size == 0) {
-            // if the attachment is not a content:// for example, a file:// URI
-            attachment.size = getSizeFromFile(contentUri, contentResolver);
+        if (name == null) {
+            name = contentUri.getLastPathSegment();
         }
 
+        // For virtual files append the inferred extension name.
+        if (isVirtual) {
+            String extension = MimeTypeMap.getSingleton()
+                    .getExtensionFromMimeType(attachment.virtualMimeType);
+            if (extension != null) {
+                name += "." + extension;
+            }
+        }
+
+        // TODO: This can't work with pipes. Fix it.
+        if (size == -1 && !isVirtual) {
+            // if the attachment is not a content:// for example, a file:// URI
+            size = getSizeFromFile(contentUri, contentResolver);
+        }
+
+        // Save the computed values into the attachment.
+        attachment.size = size;
+        attachment.setName(name);
         attachment.setContentType(contentType);
+
         return attachment;
     }
 
@@ -300,28 +371,28 @@
      * @param account
      * @param attachment the attachment to be added.
      *
-     * @return size of the attachment added.
      * @throws AttachmentFailureException if an error occurs adding the attachment.
      */
-    public long addAttachment(Account account, Attachment attachment)
+    public void addAttachment(Account account, Attachment attachment)
             throws AttachmentFailureException {
         final int maxSize = account.settings.getMaxAttachmentSize();
 
-        // Error getting the size or the size was too big.
-        if (attachment.size == -1 || attachment.size > maxSize) {
+        // The attachment size is known and it's too big.
+        if (attachment.size > maxSize) {
             throw new AttachmentFailureException(
                     "Attachment too large to attach", R.string.too_large_to_attach_single);
-        } else if ((getTotalAttachmentsSize()
+        } else if (attachment.size != -1 && (getTotalAttachmentsSize()
                 + attachment.size) > maxSize) {
             throw new AttachmentFailureException(
                     "Attachment too large to attach", R.string.too_large_to_attach_additional);
         } else {
             addAttachment(attachment);
         }
-
-        return attachment.size;
     }
 
+    /**
+     * @return size of the file or -1 if unknown.
+     */
     private static int getSizeFromFile(Uri uri, ContentResolver contentResolver) {
         int size = -1;
         ParcelFileDescriptor file = null;
@@ -339,9 +410,7 @@
                 LogUtils.w(LOG_TAG, "Error closing file opened to obtain size.");
             }
         }
-        // We only want to return a non-negative value. (ParcelFileDescriptor#getStatSize() will
-        // return -1 if the fd is not a file
-        return Math.max(size, 0);
+        return size;
     }
 
     /**
diff --git a/src/com/android/mail/compose/ComposeActivity.java b/src/com/android/mail/compose/ComposeActivity.java
index 489f6d8..3dd9e87 100644
--- a/src/com/android/mail/compose/ComposeActivity.java
+++ b/src/com/android/mail/compose/ComposeActivity.java
@@ -36,6 +36,7 @@
 import android.content.Intent;
 import android.content.Loader;
 import android.content.pm.ActivityInfo;
+import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Rect;
@@ -50,7 +51,7 @@
 import android.provider.BaseColumns;
 import android.support.v4.app.RemoteInput;
 import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ActionMode;
 import android.text.Editable;
 import android.text.Html;
@@ -137,7 +138,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
-public class ComposeActivity extends ActionBarActivity
+public class ComposeActivity extends AppCompatActivity
         implements OnClickListener, ActionBar.OnNavigationListener,
         RespondInlineListener, TextWatcher,
         AttachmentAddedOrDeletedListener, OnAccountChangedListener,
@@ -1834,12 +1835,16 @@
         addAttachments(refMessage.getAttachments());
     }
 
-    public long addAttachments(List<Attachment> attachments) {
-        long size = 0;
+    /**
+     * @return true if at least one file is attached.
+     */
+    public boolean addAttachments(List<Attachment> attachments) {
+        boolean attached = false;
         AttachmentFailureException error = null;
         for (Attachment a : attachments) {
             try {
-                size += mAttachmentsView.addAttachment(mAccount, a);
+                mAttachmentsView.addAttachment(mAccount, a);
+                attached = true;
             } catch (AttachmentFailureException e) {
                 error = e;
             }
@@ -1852,7 +1857,7 @@
                 showAttachmentTooBigToast(error.getErrorRes());
             }
         }
-        return size;
+        return attached;
     }
 
     /**
@@ -1881,33 +1886,30 @@
         }
         final String action = intent.getAction();
         if (!mAttachmentsChanged) {
-            long totalSize = 0;
+            boolean attached = false;
             if (extras.containsKey(EXTRA_ATTACHMENTS)) {
                 final String[] uris = (String[]) extras.getSerializable(EXTRA_ATTACHMENTS);
                 final ArrayList<Uri> parsedUris = Lists.newArrayListWithCapacity(uris.length);
                 for (String uri : uris) {
                     parsedUris.add(Uri.parse(uri));
                 }
-                totalSize += handleAttachmentUrisFromIntent(parsedUris);
+                attached |= handleAttachmentUrisFromIntent(parsedUris);
             }
             if (extras.containsKey(Intent.EXTRA_STREAM)) {
                 if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
                     final ArrayList<Uri> uris = extras
                             .getParcelableArrayList(Intent.EXTRA_STREAM);
-                    totalSize += handleAttachmentUrisFromIntent(uris);
+                    attached |= handleAttachmentUrisFromIntent(uris);
                 } else {
                     final Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
                     final ArrayList<Uri> uris = Lists.newArrayList(uri);
-                    totalSize += handleAttachmentUrisFromIntent(uris);
+                    attached |= handleAttachmentUrisFromIntent(uris);
                 }
             }
 
-            if (totalSize > 0) {
+            if (attached) {
                 mAttachmentsChanged = true;
                 updateSaveUi();
-
-                Analytics.getInstance().sendEvent("send_intent_with_attachments",
-                        Integer.toString(getAttachments().size()), null, totalSize);
             }
         }
     }
@@ -1932,9 +1934,9 @@
 
     /**
      * Helper function to handle a list of uris to attach.
-     * @return the total size of all successfully attached files.
+     * @return true if anything has been attached.
      */
-    private long handleAttachmentUrisFromIntent(List<Uri> uris) {
+    private boolean handleAttachmentUrisFromIntent(List<Uri> uris) {
         ArrayList<Attachment> attachments = Lists.newArrayList();
         for (Uri uri : uris) {
             try {
@@ -2034,8 +2036,8 @@
             return;
         }
 
-        final long size = handleAttachmentUrisFromIntent(Arrays.asList(contentUri));
-        if (size > 0) {
+        final boolean attached = handleAttachmentUrisFromIntent(Arrays.asList(contentUri));
+        if (attached) {
             mAttachmentsChanged = true;
             updateSaveUi();
         }
@@ -2053,11 +2055,9 @@
 
     private void addAttachmentAndUpdateView(Attachment attachment) {
         try {
-            long size = mAttachmentsView.addAttachment(mAccount, attachment);
-            if (size > 0) {
-                mAttachmentsChanged = true;
-                updateSaveUi();
-            }
+            mAttachmentsView.addAttachment(mAccount, attachment);
+            mAttachmentsChanged = true;
+            updateSaveUi();
         } catch (AttachmentFailureException e) {
             LogUtils.e(LOG_TAG, e, "Error adding attachment");
             showAttachmentTooBigToast(e.getErrorRes());
@@ -2616,7 +2616,7 @@
         if (openedFds != null) {
             final Set<String> keys = openedFds.keySet();
             for (final String key : keys) {
-                final ParcelFileDescriptor fd = openedFds.getParcelable(key);
+                final AssetFileDescriptor fd = openedFds.getParcelable(key);
                 if (fd != null) {
                     try {
                         fd.close();
@@ -2726,9 +2726,16 @@
                 continue;
             }
 
-            ParcelFileDescriptor fileDescriptor;
+            AssetFileDescriptor fileDescriptor;
             try {
-                fileDescriptor = resolver.openFileDescriptor(attachment.contentUri, "r");
+                if (attachment.virtualMimeType == null) {
+                    fileDescriptor = new AssetFileDescriptor(
+                        resolver.openFileDescriptor(attachment.contentUri, "r"), 0,
+                        AssetFileDescriptor.UNKNOWN_LENGTH);
+                } else {
+                    fileDescriptor = resolver.openTypedAssetFileDescriptor(
+                            attachment.contentUri, attachment.virtualMimeType, null, null);
+                }
             } catch (FileNotFoundException e) {
                 LogUtils.e(LOG_TAG, e, "Exception attempting to open attachment");
                 fileDescriptor = null;
@@ -3435,7 +3442,6 @@
     @SuppressLint("NewApi")
     private void doAttach(String type) {
         Intent i = new Intent(Intent.ACTION_GET_CONTENT);
-        i.addCategory(Intent.CATEGORY_OPENABLE);
         i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
         i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
         i.setType(type);
diff --git a/src/com/android/mail/providers/Attachment.java b/src/com/android/mail/providers/Attachment.java
index b8e86ed..4ed33f8 100644
--- a/src/com/android/mail/providers/Attachment.java
+++ b/src/com/android/mail/providers/Attachment.java
@@ -160,6 +160,14 @@
      */
     public String providerData;
 
+    /**
+     * Streamable mime type of the attachment in case it's a virtual file.
+     *
+     * Might be null. If null, then the default type (contentType) is assumed
+     * to be streamable.
+     */
+    public String virtualMimeType;
+
     private transient Uri mIdentifierUri;
 
     /**
@@ -186,6 +194,7 @@
         supportsDownloadAgain = in.readInt() == 1;
         type = in.readInt();
         flags = in.readInt();
+        virtualMimeType = in.readString();
     }
 
     public Attachment(Cursor cursor) {
@@ -211,6 +220,7 @@
                 cursor.getColumnIndex(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN)) == 1;
         type = cursor.getInt(cursor.getColumnIndex(AttachmentColumns.TYPE));
         flags = cursor.getInt(cursor.getColumnIndex(AttachmentColumns.FLAGS));
+        virtualMimeType = cursor.getString(cursor.getColumnIndex(AttachmentColumns.VIRTUAL_MIME_TYPE));
     }
 
     public Attachment(JSONObject srcJson) {
@@ -228,6 +238,7 @@
         supportsDownloadAgain = srcJson.optBoolean(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, true);
         type = srcJson.optInt(AttachmentColumns.TYPE);
         flags = srcJson.optInt(AttachmentColumns.FLAGS);
+        virtualMimeType = srcJson.optString(AttachmentColumns.VIRTUAL_MIME_TYPE, null);
     }
 
     /**
@@ -257,6 +268,7 @@
             type = inline ? AttachmentType.INLINE_CURRENT_MESSAGE : AttachmentType.STANDARD;
             partId = cid;
             flags = 0;
+            virtualMimeType = null;
 
             // insert attachment into content provider so that we can open the file
             final ContentResolver resolver = context.getContentResolver();
@@ -303,6 +315,7 @@
         type = values.getAsInteger(AttachmentColumns.TYPE);
         flags = values.getAsInteger(AttachmentColumns.FLAGS);
         partId = values.getAsString(AttachmentColumns.CONTENT_ID);
+        virtualMimeType = values.getAsString(AttachmentColumns.VIRTUAL_MIME_TYPE);
     }
 
     /**
@@ -328,6 +341,7 @@
         values.put(AttachmentColumns.TYPE, type);
         values.put(AttachmentColumns.FLAGS, flags);
         values.put(AttachmentColumns.CONTENT_ID, partId);
+        values.put(AttachmentColumns.VIRTUAL_MIME_TYPE, virtualMimeType);
 
         return values;
     }
@@ -348,6 +362,7 @@
         dest.writeInt(supportsDownloadAgain ? 1 : 0);
         dest.writeInt(type);
         dest.writeInt(flags);
+        dest.writeString(virtualMimeType);
     }
 
     public JSONObject toJSON() throws JSONException {
@@ -367,6 +382,7 @@
         result.put(AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, supportsDownloadAgain);
         result.put(AttachmentColumns.TYPE, type);
         result.put(AttachmentColumns.FLAGS, flags);
+        result.put(AttachmentColumns.VIRTUAL_MIME_TYPE, virtualMimeType);
 
         return result;
     }
diff --git a/src/com/android/mail/providers/UIProvider.java b/src/com/android/mail/providers/UIProvider.java
index 783e4f3..3bb6da5 100644
--- a/src/com/android/mail/providers/UIProvider.java
+++ b/src/com/android/mail/providers/UIProvider.java
@@ -1958,7 +1958,8 @@
         AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN,
         AttachmentColumns.TYPE,
         AttachmentColumns.FLAGS,
-        AttachmentColumns.CONTENT_ID
+        AttachmentColumns.CONTENT_ID,
+        AttachmentColumns.VIRTUAL_MIME_TYPE
     };
     public static final int ATTACHMENT_NAME_COLUMN = 0;
     public static final int ATTACHMENT_SIZE_COLUMN = 1;
@@ -1974,6 +1975,7 @@
     public static final int ATTACHMENT_TYPE_COLUMN = 11;
     public static final int ATTACHMENT_FLAGS_COLUMN = 12;
     public static final int ATTACHMENT_CONTENT_ID_COLUMN = 13;
+    public static final int ATTACHMENT_VIRTUAL_MIME_TYPE_COLUMN = 14;
 
     /** Separates attachment info parts in strings in the database. */
     public static final String ATTACHMENT_INFO_SEPARATOR = "\n"; // use to join
@@ -2160,6 +2162,11 @@
          */
         public static final String CONTENT_ID = "contentId";
 
+        /**
+         * Holds a streamable mime type for this attachment if it's a virtual file.
+         */
+        public static final String VIRTUAL_MIME_TYPE = "streamableMimeType";
+
         private AttachmentColumns() {}
     }
 
diff --git a/src/com/android/mail/ui/AbstractMailActivity.java b/src/com/android/mail/ui/AbstractMailActivity.java
index 00d2f5f..5cf4128 100644
--- a/src/com/android/mail/ui/AbstractMailActivity.java
+++ b/src/com/android/mail/ui/AbstractMailActivity.java
@@ -20,7 +20,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.StrictMode;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -37,7 +37,7 @@
  * <p>In the Gmail codebase, this was called GmailBaseActivity</p>
  *
  */
-public abstract class AbstractMailActivity extends ActionBarActivity implements RestrictedActivity {
+public abstract class AbstractMailActivity extends AppCompatActivity implements RestrictedActivity {
 
     private final UiHandler mUiHandler = new UiHandler();
 
diff --git a/src/com/android/mail/ui/AccountFeedbackActivity.java b/src/com/android/mail/ui/AccountFeedbackActivity.java
index 89a736d..e3cb592 100644
--- a/src/com/android/mail/ui/AccountFeedbackActivity.java
+++ b/src/com/android/mail/ui/AccountFeedbackActivity.java
@@ -21,7 +21,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
 import android.view.Menu;
 import android.view.MenuItem;
 
@@ -37,7 +37,7 @@
  * as well as showing settings/help/send feedback in the action
  * overflow menu.
  */
-public abstract class AccountFeedbackActivity extends ActionBarActivity
+public abstract class AccountFeedbackActivity extends AppCompatActivity
         implements ConversationAccountController, AccountLoadCallbacks.AccountLoadCallbackListener {
     public static final String EXTRA_ACCOUNT_URI = "extra-account-uri";
 
diff --git a/src/com/android/mail/ui/ActionableToastBar.java b/src/com/android/mail/ui/ActionableToastBar.java
index 8ad9828..c6bf990 100644
--- a/src/com/android/mail/ui/ActionableToastBar.java
+++ b/src/com/android/mail/ui/ActionableToastBar.java
@@ -459,4 +459,4 @@
     public interface ActionClickedListener {
         public void onActionClicked(Context context);
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/mail/ui/FolderSelectionActivity.java b/src/com/android/mail/ui/FolderSelectionActivity.java
index 53e3539..09dae1a 100644
--- a/src/com/android/mail/ui/FolderSelectionActivity.java
+++ b/src/com/android/mail/ui/FolderSelectionActivity.java
@@ -26,7 +26,7 @@
 import android.database.DataSetObservable;
 import android.database.DataSetObserver;
 import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
@@ -50,7 +50,7 @@
 /**
  * This activity displays the list of available folders for the current account.
  */
-public class FolderSelectionActivity extends ActionBarActivity implements OnClickListener,
+public class FolderSelectionActivity extends AppCompatActivity implements OnClickListener,
         DialogInterface.OnClickListener, ControllableActivity,
         FolderSelector {
     public static final String EXTRA_ACCOUNT_SHORTCUT = "account-shortcut";
diff --git a/src/com/android/mail/ui/MailboxSelectionActivity.java b/src/com/android/mail/ui/MailboxSelectionActivity.java
index e524083..9605f09 100644
--- a/src/com/android/mail/ui/MailboxSelectionActivity.java
+++ b/src/com/android/mail/ui/MailboxSelectionActivity.java
@@ -36,7 +36,7 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
@@ -49,7 +49,7 @@
  * An activity that shows the list of all the available accounts and return the
  * one selected in onResult().
  */
-public class MailboxSelectionActivity extends ActionBarActivity implements OnClickListener,
+public class MailboxSelectionActivity extends AppCompatActivity implements OnClickListener,
         LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener {
 
     // Used to save our instance state
diff --git a/src/com/android/mail/ui/RestrictedActivity.java b/src/com/android/mail/ui/RestrictedActivity.java
index ed91b7b..364f677 100644
--- a/src/com/android/mail/ui/RestrictedActivity.java
+++ b/src/com/android/mail/ui/RestrictedActivity.java
@@ -36,9 +36,9 @@
 
 /**
  * {@link RestrictedActivity} gives access to a subset of
- * {@link android.support.v7.app.ActionBarActivity} methods.
+ * {@link android.support.v7.app.AppCompatActivity} methods.
  * These methods match the signatures from
- * {@link android.support.v7.app.ActionBarActivity}.
+ * {@link android.support.v7.app.AppCompatActivity}.
  */
 public interface RestrictedActivity {
     /*
@@ -49,7 +49,7 @@
     /**
      * @see android.app.Activity#findViewById(int)
      */
-    View findViewById(int id);
+    <T extends View> T findViewById(int id);
 
     /**
      * @see android.app.Activity#finish()
@@ -57,7 +57,7 @@
     void finish();
 
     /**
-     * @see android.support.v7.app.ActionBarActivity#getSupportActionBar()
+     * @see android.support.v7.app.AppCompatActivity#getSupportActionBar()
      */
     ActionBar getSupportActionBar();
 
@@ -102,7 +102,7 @@
     Window getWindow();
 
     /**
-     * @see android.support.v7.app.ActionBarActivity#supportInvalidateOptionsMenu()
+     * @see android.support.v7.app.AppCompatActivity#supportInvalidateOptionsMenu()
      */
     void supportInvalidateOptionsMenu();
 
@@ -147,7 +147,7 @@
     void showDialog(int id);
 
     /**
-     * @see android.support.v7.app.ActionBarActivity#startSupportActionMode(ActionMode.Callback)
+     * @see android.support.v7.app.AppCompatActivity#startSupportActionMode(ActionMode.Callback)
      */
     ActionMode startSupportActionMode(ActionMode.Callback callback);
 
diff --git a/src/com/android/mail/utils/AttachmentUtils.java b/src/com/android/mail/utils/AttachmentUtils.java
index 4837f7a..8cdaef8 100644
--- a/src/com/android/mail/utils/AttachmentUtils.java
+++ b/src/com/android/mail/utils/AttachmentUtils.java
@@ -17,6 +17,8 @@
 
 import android.app.DownloadManager;
 import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetFileDescriptor.AutoCloseInputStream;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.os.Bundle;
@@ -187,13 +189,13 @@
         try {
             final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-kk:mm:ss");
             file = File.createTempFile(dateFormat.format(new Date()), ".attachment", cacheDir);
-            final ParcelFileDescriptor fileDescriptor = attachmentFds != null
-                    && attachment.contentUri != null ? (ParcelFileDescriptor) attachmentFds
+            final AssetFileDescriptor fileDescriptor = attachmentFds != null
+                    && attachment.contentUri != null ? (AssetFileDescriptor) attachmentFds
                     .getParcelable(attachment.contentUri.toString())
                     : null;
             if (fileDescriptor != null) {
                 // Get the input stream from the file descriptor
-                inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
+                inputStream = new AutoCloseInputStream(fileDescriptor);
             } else {
                 if (attachment.contentUri == null) {
                     // The contentUri of the attachment is null.  This can happen when sending a
@@ -203,7 +205,15 @@
                     throw new FileNotFoundException("Missing contentUri in attachment");
                 }
                 // Attempt to open the file
-                inputStream = context.getContentResolver().openInputStream(attachment.contentUri);
+                if (attachment.virtualMimeType == null) {
+                    inputStream = context.getContentResolver().openInputStream(attachment.contentUri);
+                } else {
+                    AssetFileDescriptor fd = context.getContentResolver().openTypedAssetFileDescriptor(
+                            attachment.contentUri, attachment.virtualMimeType, null, null);
+                    if (fd != null) {
+                        inputStream = new AutoCloseInputStream(fd);
+                    }
+                }
             }
             outputStream = new FileOutputStream(file);
             final long now = SystemClock.elapsedRealtime();
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index a9c93bf..4efbccc 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -36,6 +36,7 @@
 import android.os.Bundle;
 import android.provider.Browser;
 import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
 import android.text.SpannableString;
 import android.text.Spanned;
 import android.text.TextUtils;
@@ -144,6 +145,10 @@
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
     }
 
+    public static boolean isRunningNOrLater() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+    }
+
     /**
      * @return Whether we are running on a low memory device.  This is used to disable certain
      * memory intensive features in the app.
@@ -1069,6 +1074,7 @@
      * Applies the given appearance on the given subString, and inserts that as a parameter in the
      * given parentString.
      */
+    @VisibleForTesting
     public static Spanned insertStringWithStyle(Context context,
             String entireString, String subString, int appearance) {
         final int index = entireString.indexOf(subString);
diff --git a/tests/Android.mk b/tests/Android.mk
index 902d31f..4762862 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -31,7 +31,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
 LOCAL_AAPT_FLAGS := --auto-add-overlay
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/src/com/android/mail/browse/ConversationCursorTests.java b/tests/src/com/android/mail/browse/ConversationCursorTests.java
index bd8c928..5034526 100644
--- a/tests/src/com/android/mail/browse/ConversationCursorTests.java
+++ b/tests/src/com/android/mail/browse/ConversationCursorTests.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.OperationApplicationException;
 import android.net.Uri;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.test.ProviderTestCase2;
 import android.test.mock.MockContentResolver;
@@ -47,6 +48,7 @@
 
     public ConversationCursorTests() {
         super(TestProvider.class, TestProvider.AUTHORITY);
+        Looper.prepare();
         mActivity = new Activity() {
             @Override
             public ContentResolver getContentResolver() {
@@ -60,7 +62,6 @@
         super.setUp();
         mMockContext = getMockContext();
         mMockResolver = (MockContentResolver)mMockContext.getContentResolver();
-        mMockResolver.addProvider(TestProvider.AUTHORITY, new TestProvider(mMockContext));
     }
 
     @Override
diff --git a/tests/src/com/android/mail/browse/EmlMessageLoaderTest.java b/tests/src/com/android/mail/browse/EmlMessageLoaderTest.java
index 8e4b803..54f1a25 100644
--- a/tests/src/com/android/mail/browse/EmlMessageLoaderTest.java
+++ b/tests/src/com/android/mail/browse/EmlMessageLoaderTest.java
@@ -54,8 +54,8 @@
 
     @SmallTest
     public void testLoadingBlankEml() throws Exception {
-        final Uri emptyEmlUri = TestProvider.uri(
-                new Uri.Builder().scheme("content").authority("empty").path("empty").build());
+        final Uri emptyEmlUri = new Uri.Builder().scheme("content").authority(
+                TestProvider.AUTHORITY).path("empty").build();
         final ContentValues cv = new ContentValues(2);
         cv.put(OpenableColumns.DISPLAY_NAME, "Empty.eml");
         cv.put(OpenableColumns.SIZE, 0);
diff --git a/tests/src/com/android/mail/browse/TestProvider.java b/tests/src/com/android/mail/browse/TestProvider.java
index df305b5..19d5ec3 100644
--- a/tests/src/com/android/mail/browse/TestProvider.java
+++ b/tests/src/com/android/mail/browse/TestProvider.java
@@ -21,6 +21,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.UriMatcher;
+import android.content.pm.ProviderInfo;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
@@ -54,9 +55,6 @@
  * delete() and update() do not allow non-null selection or selectionArgs arguments; the presence
  * of these will result in an UnsupportedOperationException
  *
- * NOTE: When using any operation other than applyBatch, the Uri to be used must be created with
- * MockProvider.uri(yourUri). This guarantees that the operation is sent to MockProvider
- *
  * NOTE: MockProvider only simulates direct storage/retrieval of rows; it does not (and can not)
  * simulate other actions (e.g. creation of ancillary data) that the actual provider might perform
  *
@@ -64,6 +62,7 @@
  **/
 public class TestProvider extends ContentProvider {
     public static final String AUTHORITY = "com.android.mail.mock.provider";
+
     /* package */static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 
     /* package */static final int TABLE = 100;
@@ -77,7 +76,9 @@
 
     public TestProvider(Context context) {
         this();
-        attachInfo(context, null);
+        ProviderInfo info = new ProviderInfo();
+        info.authority = AUTHORITY;
+        attachInfo(context, info);
     }
 
     // We'll store our values here
@@ -85,17 +86,6 @@
     // And we'll generate new id's from here
     long mMockId = 1;
 
-    /**
-     * Create a Uri for MockProvider from a given Uri
-     *
-     * @param uri the Uri from which the MockProvider Uri will be created
-     * @return a Uri that can be used with MockProvider
-     */
-    public static Uri uri(Uri uri) {
-        return new Uri.Builder().scheme("content").authority(AUTHORITY)
-                .path(uri.getPath().substring(1)).build();
-    }
-
     @Override
     public int delete(Uri uri, String selection, String[] selectionArgs) {
         if (selection != null || selectionArgs != null) {
@@ -144,7 +134,7 @@
         if (selection != null || selectionArgs != null || sortOrder != null || projection == null) {
             throw new UnsupportedOperationException();
         }
-        final int match = sURIMatcher.match(uri(uri));
+        final int match = sURIMatcher.match(uri);
         ArrayList<ContentValues> valuesList = new ArrayList<ContentValues>();
         switch (match) {
             case TABLE:
@@ -182,7 +172,7 @@
         if (selection != null || selectionArgs != null) {
             throw new UnsupportedOperationException();
         }
-        final int match = sURIMatcher.match(uri(uri));
+        final int match = sURIMatcher.match(uri);
         ArrayList<ContentValues> updateValuesList = new ArrayList<ContentValues>();
         String path = uri.getPath();
         switch (match) {
diff --git a/tests/src/com/android/mail/browse/TestProviderTests.java b/tests/src/com/android/mail/browse/TestProviderTests.java
index 0e26980..be45655 100644
--- a/tests/src/com/android/mail/browse/TestProviderTests.java
+++ b/tests/src/com/android/mail/browse/TestProviderTests.java
@@ -37,7 +37,6 @@
     Context mMockContext;
     MockContentResolver mMockResolver;
 
-    private static final String CANHAZ_AUTHORITY = "com.android.canhaz";
     private static final String PONY_TABLE = "pony";
     private static final String PONY_COLUMN_NAME = "name";
     private static final String PONY_COLUMN_TYPE = "type";
@@ -60,7 +59,6 @@
         super.setUp();
         mMockContext = getMockContext();
         mMockResolver = (MockContentResolver)mMockContext.getContentResolver();
-        mMockResolver.addProvider(CANHAZ_AUTHORITY, new TestProvider(mMockContext));
     }
 
     @Override
@@ -79,8 +77,7 @@
 
     private ContentProviderResult[] setupPonies() throws RemoteException,
             OperationApplicationException {
-        // The Uri is content://com.android.canhaz/pony
-        Uri uri = new Uri.Builder().scheme("content").authority(CANHAZ_AUTHORITY)
+        Uri uri = new Uri.Builder().scheme("content").authority(TestProvider.AUTHORITY)
             .path(PONY_TABLE).build();
         // Our array of CPO's to be used with applyBatch
         ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
@@ -95,12 +92,11 @@
     }
 
     private static Uri getPonyUri() {
-        return new Uri.Builder().scheme("content").authority(CANHAZ_AUTHORITY)
+        return new Uri.Builder().scheme("content").authority(TestProvider.AUTHORITY)
             .path(PONY_TABLE).build();
     }
 
     public void testInsertQueryandDelete() throws RemoteException, OperationApplicationException {
-        // The Uri is content://com.android.canhaz/pony
         ContentProviderResult[] results = setupPonies();
         Uri uri = getPonyUri();
 
@@ -108,10 +104,9 @@
         assertNotNull(results);
         assertEquals(2, results.length);
         // Make sure that we've created matcher entries for pony and pony/#
-        assertEquals(TestProvider.TABLE, TestProvider.sURIMatcher.match(TestProvider.uri(uri)));
-        assertEquals(TestProvider.RECORD,
-                TestProvider.sURIMatcher.match(TestProvider.uri(results[0].uri)));
-        Cursor c = mMockResolver.query(TestProvider.uri(uri), PONY_PROJECTION, null, null, null);
+        assertEquals(TestProvider.TABLE, TestProvider.sURIMatcher.match(uri));
+        assertEquals(TestProvider.RECORD, TestProvider.sURIMatcher.match(results[0].uri));
+        Cursor c = mMockResolver.query(uri, PONY_PROJECTION, null, null, null);
         assertNotNull(c);
         assertEquals(2, c.getCount());
         long eliseId = -1;
@@ -138,9 +133,9 @@
         assertNotSame(-1, eliseId);
         assertNotSame(-1, flickaId);
         // Delete the elise record
-        assertEquals(1, mMockResolver.delete(ContentUris.withAppendedId(TestProvider.uri(uri),
+        assertEquals(1, mMockResolver.delete(ContentUris.withAppendedId(uri,
                 eliseId), null, null));
-        c = mMockResolver.query(TestProvider.uri(uri), PONY_PROJECTION, null, null, null);
+        c = mMockResolver.query(uri, PONY_PROJECTION, null, null, null);
         assertNotNull(c);
         // There should be one left (Flicka)
         assertEquals(1, c.getCount());
@@ -149,17 +144,16 @@
     }
 
     public void testUpdate() throws RemoteException, OperationApplicationException {
-        // The Uri is content://com.android.canhaz/pony
         Uri uri = getPonyUri();
         setupPonies();
-        Cursor c = mMockResolver.query(TestProvider.uri(uri), PONY_PROJECTION, null, null, null);
+        Cursor c = mMockResolver.query(uri, PONY_PROJECTION, null, null, null);
         assertNotNull(c);
         assertEquals(2, c.getCount());
         // Give all the ponies 5 legs
         ContentValues cv = new ContentValues();
         cv.put(PONY_COLUMN_LEGS, 5);
-        assertEquals(2, mMockResolver.update(TestProvider.uri(uri), cv, null, null));
-        c = mMockResolver.query(TestProvider.uri(uri), PONY_PROJECTION, null, null, null);
+        assertEquals(2, mMockResolver.update(uri, cv, null, null));
+        c = mMockResolver.query(uri, PONY_PROJECTION, null, null, null);
         assertNotNull(c);
         // We should still have two records, and each should have 5 legs, but otherwise be the same
         assertEquals(2, c.getCount());