32764144 Security Vulnerability - heap buffer overflow in libgiftranscode.so in colorMap->Colors[colorIndex] am: 6f763fef7a am: 9f00add2fb am: 71d4913462
am: 82fa311e9a

Change-Id: Ifd7c99312f526e379268f5f12a5ff61d95a90996
diff --git a/Android.mk b/Android.mk
index 725c89d..e2ed18b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -75,8 +75,6 @@
     LOCAL_PROGUARD_FLAG_FILES += proguard-release.flags
 endif
 
-LOCAL_JACK_ENABLED := disabled
-
 LOCAL_PACKAGE_NAME := messaging
 
 LOCAL_CERTIFICATE := platform
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4b16a82..22459c4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -18,7 +18,7 @@
     package="com.android.messaging"
     android:installLocation="internalOnly">
 
-    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23" />
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="24" />
 
     <!-- Application holds CPU wakelock while working in background -->
     <uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/jni/GifTranscoder.cpp b/jni/GifTranscoder.cpp
index 0e83982..112feca 100644
--- a/jni/GifTranscoder.cpp
+++ b/jni/GifTranscoder.cpp
@@ -508,11 +508,11 @@
 
 GifFilesCloser::~GifFilesCloser() {
     if (mGifIn) {
-        DGifCloseFile(mGifIn);
+        DGifCloseFile(mGifIn, NULL);
         mGifIn = NULL;
     }
     if (mGifOut) {
-        EGifCloseFile(mGifOut);
+        EGifCloseFile(mGifOut, NULL);
         mGifOut = NULL;
     }
 }
diff --git a/proguard.flags b/proguard.flags
index 759f2d4..76b033c 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -23,7 +23,7 @@
 }
 
 # Keep methods that have the @VisibleForAnimation annotation
--keep @interface com.android.messaging.annotation.VisibleForAnimation
+-keep interface com.android.messaging.annotation.VisibleForAnimation
 -keepclassmembers class * {
   @com.android.messaging.annotation.VisibleForAnimation *;
 }
diff --git a/res/drawable-hdpi/ic_work_profile.png b/res/drawable-hdpi/ic_work_profile.png
new file mode 100644
index 0000000..5c2e457
--- /dev/null
+++ b/res/drawable-hdpi/ic_work_profile.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_work_profile.png b/res/drawable-mdpi/ic_work_profile.png
new file mode 100644
index 0000000..b7ffd2b
--- /dev/null
+++ b/res/drawable-mdpi/ic_work_profile.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_work_profile.png b/res/drawable-xhdpi/ic_work_profile.png
new file mode 100644
index 0000000..b47b485
--- /dev/null
+++ b/res/drawable-xhdpi/ic_work_profile.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_work_profile.png b/res/drawable-xxhdpi/ic_work_profile.png
new file mode 100644
index 0000000..de257e7
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_work_profile.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_work_profile.png b/res/drawable-xxxhdpi/ic_work_profile.png
new file mode 100644
index 0000000..72292bd
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_work_profile.png
Binary files differ
diff --git a/res/layout/contact_list_item_view.xml b/res/layout/contact_list_item_view.xml
index 3015ae3..71a7370 100644
--- a/res/layout/contact_list_item_view.xml
+++ b/res/layout/contact_list_item_view.xml
@@ -63,15 +63,30 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical" >
-        <TextView
-            android:id="@+id/contact_name"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:paddingBottom="4dp"
-            android:singleLine="true"
-            android:maxLines="1"
-            android:ellipsize="end"
-            style="@style/ContactListItem" />
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+            <TextView
+                android:id="@+id/contact_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:paddingBottom="4dp"
+                android:singleLine="true"
+                android:maxLines="1"
+                android:ellipsize="end"
+                style="@style/ContactListItem" />
+
+            <ImageView android:id="@+id/work_profile_icon"
+                android:src="@drawable/ic_work_profile"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:scaleType="center"
+                android:layout_gravity="center_vertical"
+                android:visibility="gone" />
+        </LinearLayout>
 
         <LinearLayout
             android:orientation="horizontal"
diff --git a/res/layout/work_directory_header.xml b/res/layout/work_directory_header.xml
new file mode 100644
index 0000000..3c882f5
--- /dev/null
+++ b/res/layout/work_directory_header.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF A`NY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Layout used for list section separators. -->
+<TextView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:text="@string/work_directory_display_name"
+        style="@style/DirectoryHeaderStyle"
+        />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 270ea9e..5ff0eb7 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -188,4 +188,10 @@
     <dimen name="fastscroll_preview_margin_left_right">8dp</dimen>
     <dimen name="fastscroll_preview_text_size">24sp</dimen>
 
+    <dimen name="directory_header_padding_start">16dp</dimen>
+    <dimen name="directory_header_padding_end">32dp</dimen>
+    <dimen name="directory_header_padding_top">18dp</dimen>
+    <dimen name="directory_header_padding_bottom">8dp</dimen>
+    <dimen name="directory_header_text_size">14sp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ec675ef..012d87f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -152,7 +152,7 @@
     <!-- While sending a message display this message. -->
     <string name="message_status_sending">Sending&#8230;</string>
     <!-- When sending a message failed display this message. -->
-    <string name="message_status_send_failed">Not sent. Touch to try again.</string>
+    <string name="message_status_send_failed">Not sent. Tap to try again.</string>
     <!-- When retrying sending for a message. -->
     <string name="message_status_send_retrying">Not sent. Trying again&#8230;</string>
     <!-- When showing resend action display this message. -->
@@ -171,9 +171,9 @@
     <!-- Title line for MMS which failed to download. -->
     <string name="message_title_download_failed">Couldn\'t download</string>
     <!-- Timestamp line for MMS which failed to download. -->
-    <string name="message_status_download_failed">Touch to try again</string>
+    <string name="message_status_download_failed">Tap to try again</string>
     <!-- Timestamp line for MMS which failed to download. -->
-    <string name="message_status_download">Touch to download</string>
+    <string name="message_status_download">Tap to download</string>
     <!-- Timestamp line for MMS which is selected. -->
     <string name="message_status_download_action">Download or delete</string>
     <!-- Timestamp line to display while downloading a message. -->
@@ -296,7 +296,7 @@
     <!-- Error occurred while recording audio -->
     <string name="audio_recording_error"> Couldn\'t save audio. Try again.</string>
     <!-- Hint text on the audio recorder that instructs user how to start recording -->
-    <string name="audio_picker_hint_text">Touch &amp; hold</string>
+    <string name="audio_picker_hint_text">Tap &amp; hold</string>
 
     <!-- An enumeration comma for separating multiple names in notifications. [CHAR LIMIT=2] -->
     <string name="enumeration_comma">,\u0020</string>
@@ -348,7 +348,7 @@
     <string name="notification_on_toast_message">Notifications turned on</string>
 
     <!-- Toast shown when the user tries to send a message, and then sets Bugle as the default SMS app. -->
-    <string name="toast_after_setting_default_sms_app_for_message_send">All set. Touch Send again.</string>
+    <string name="toast_after_setting_default_sms_app_for_message_send">All set. Tap Send again.</string>
     <!-- Toast shown when the user successfully sets Bugle as the default SMS app. -->
     <string name="toast_after_setting_default_sms_app">Messaging successfully set as the default SMS app.</string>
 
@@ -378,7 +378,7 @@
     <string name="group_outgoing_failed_message_prefix">Failed message to <xliff:g id="group">%s</xliff:g>: <xliff:g id="message">%s</xliff:g>. Time: <xliff:g id="time">%s</xliff:g>.</string>
     <string name="group_outgoing_successful_message_prefix">Message to <xliff:g id="group">%s</xliff:g>: <xliff:g id="message">%s</xliff:g>. Time: <xliff:g id="time">%s</xliff:g>.</string>
     <!-- Accessibility description for conversation list for failed messages -->
-    <string name="failed_message_content_description">Failed message. Touch to retry.</string>
+    <string name="failed_message_content_description">Failed message. Tap to retry.</string>
 
     <!-- Accessibility name for a group conversation -->
     <string name="group_conversation_description">Conversation with <xliff:g id="participants">%s</xliff:g></string>
@@ -905,7 +905,7 @@
     <string name="send_button_long_click_description_with_sim_selector">Select SIM or edit subject</string>
 
     <!-- Content description for the audio record view -->
-    <string name="audio_record_view_content_description">Touch and hold to record audio</string>
+    <string name="audio_record_view_content_description">Tap &amp; hold to record audio</string>
 
     <!-- Content description for new conversation button in desktop widget -->
     <string name="widget_new_conversation_content_description">Start new conversation</string>
@@ -931,7 +931,7 @@
     <string name="conversation_deleted">Conversation deleted</string>
     <!-- Displayed when user adds a new conversation widget. Tapping on the widget in this
           mode will bring user to the conversation selection screen -->
-    <string name="tap_to_configure">Conversation deleted. Touch to show a different Messaging conversation</string>
+    <string name="tap_to_configure">Conversation deleted. Tap to show a different Messaging conversation</string>
 
     <!-- Toast message telling the user that someone was blocked -->
     <string name="update_destination_blocked">Blocked</string>
@@ -973,4 +973,6 @@
 
     <!-- The accessibility text read when the sim chooser pops up to read the current selected sim -->
     <string name="selected_sim_content_message"><xliff:g id="selected_sim">%s</xliff:g> selected</string>
+
+    <string name="work_directory_display_name">Work Profile contacts</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d45f2e1..582c755 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -623,4 +623,14 @@
         <item name="android:textColor">@android:color/white</item>
         <item name="android:gravity">center</item>
     </style>
+    <style name="DirectoryHeaderStyle">
+        <item name="android:paddingLeft">@dimen/directory_header_padding_start</item>
+        <item name="android:paddingStart">@dimen/directory_header_padding_start</item>
+        <item name="android:paddingRight">@dimen/directory_header_padding_end</item>
+        <item name="android:paddingEnd">@dimen/directory_header_padding_end</item>
+        <item name="android:paddingTop">@dimen/directory_header_padding_top</item>
+        <item name="android:paddingBottom">@dimen/directory_header_padding_bottom</item>
+        <item name="android:textSize">@dimen/directory_header_text_size</item>
+        <item name="android:textStyle">bold</item>
+    </style>
 </resources>
diff --git a/src/android/support/v7/mms/pdu/EncodedStringValue.java b/src/android/support/v7/mms/pdu/EncodedStringValue.java
index d8fcacf..6a914ae 100644
--- a/src/android/support/v7/mms/pdu/EncodedStringValue.java
+++ b/src/android/support/v7/mms/pdu/EncodedStringValue.java
@@ -145,7 +145,7 @@
             	}
             	try {
                     return new String(mData, CharacterSets.MIMENAME_ISO_8859_1);
-                } catch (UnsupportedEncodingException _) {
+                } catch (UnsupportedEncodingException e2) {
                     return new String(mData); // system default encoding.
                 }
             }
@@ -216,7 +216,7 @@
             try {
                 ret[i] = new EncodedStringValue(mCharacterSet,
                         temp[i].getBytes());
-            } catch (NullPointerException _) {
+            } catch (NullPointerException e) {
                 // Can't arrive here
                 return null;
             }
diff --git a/src/com/android/messaging/datamodel/data/ContactListItemData.java b/src/com/android/messaging/datamodel/data/ContactListItemData.java
index dcc7e20..b9c7e85 100644
--- a/src/com/android/messaging/datamodel/data/ContactListItemData.java
+++ b/src/com/android/messaging/datamodel/data/ContactListItemData.java
@@ -46,6 +46,9 @@
     // existing chip for which we show full contact detail for the selected contact).
     private boolean mSingleRecipient;
 
+    // Is the contact in managed profile.
+    private boolean mIsWorkContact;
+
     /**
      * Bind to a contact cursor in the contact list.
      */
@@ -77,6 +80,8 @@
         mRecipientEntry = ContactUtil.createRecipientEntry(displayName,
                 DisplayNameSources.STRUCTURED_NAME, destination, destinationType, destinationLabel,
                 contactId, lookupKey, dataId, photoThumbnailUri, isFirstLevel);
+
+        mIsWorkContact = ContactUtil.isEnterpriseContactId(contactId);
     }
 
     /**
@@ -84,13 +89,15 @@
      * optional styled name & destination for showing bold search match.
      */
     public void bind(final RecipientEntry entry, final CharSequence styledName,
-            final CharSequence styledDestination, final boolean singleRecipient) {
+                     final CharSequence styledDestination, final boolean singleRecipient,
+                     final boolean isWorkContact) {
         Assert.isTrue(entry.isValid());
         mRecipientEntry = entry;
         mStyledName = styledName;
         mStyledDestination = styledDestination;
         mAlphabetHeader = null;
         mSingleRecipient = singleRecipient;
+        mIsWorkContact = isWorkContact;
     }
 
     public CharSequence getDisplayName() {
@@ -157,4 +164,11 @@
     public RecipientEntry getRecipientEntry() {
         return mRecipientEntry;
     }
+
+    /**
+     * @return whether the contact is in managed profile.
+     */
+    public boolean getIsWorkContact() {
+        return mIsWorkContact;
+    }
 }
diff --git a/src/com/android/messaging/ui/animation/PopupTransitionAnimation.java b/src/com/android/messaging/ui/animation/PopupTransitionAnimation.java
index 21529c6..5fc2a19 100644
--- a/src/com/android/messaging/ui/animation/PopupTransitionAnimation.java
+++ b/src/com/android/messaging/ui/animation/PopupTransitionAnimation.java
@@ -79,8 +79,7 @@
         mDestRect = new Rect();
         mPopupRect = new Rect();
         mActionBarRect = new Rect();
-        final Activity activity = (Activity) viewToAnimate.getRootView().getContext();
-        mActionBarView = activity.getWindow().getDecorView().findViewById(
+        mActionBarView = viewToAnimate.getRootView().findViewById(
                 android.support.v7.appcompat.R.id.action_bar);
         mRectEvaluator = RectEvaluatorCompat.create();
         setDuration(UiUtils.MEDIAPICKER_TRANSITION_DURATION);
diff --git a/src/com/android/messaging/ui/contact/ContactDropdownLayouter.java b/src/com/android/messaging/ui/contact/ContactDropdownLayouter.java
index 7df62de..051ebeb 100644
--- a/src/com/android/messaging/ui/contact/ContactDropdownLayouter.java
+++ b/src/com/android/messaging/ui/contact/ContactDropdownLayouter.java
@@ -34,6 +34,7 @@
 import com.android.messaging.util.Assert;
 import com.android.messaging.util.AvatarUriUtil;
 import com.android.messaging.util.ContactRecipientEntryUtils;
+import com.android.messaging.util.ContactUtil;
 
 /**
  * An implementation for {@link DropdownChipLayouter}. Layouts the dropdown
@@ -99,8 +100,9 @@
         Assert.isTrue(itemView instanceof ContactListItemView);
         final ContactListItemView contactListItemView = (ContactListItemView) itemView;
         contactListItemView.setImageClickHandlerDisabled(true);
+        boolean isWorkContact = ContactUtil.isEnterpriseContactId(entry.getContactId());
         contactListItemView.bind(entry, styledResults[0], styledResults[1],
-                mClivHostInterface, (type == AdapterType.SINGLE_RECIPIENT));
+                mClivHostInterface, (type == AdapterType.SINGLE_RECIPIENT), isWorkContact);
         return itemView;
     }
 
diff --git a/src/com/android/messaging/ui/contact/ContactListItemView.java b/src/com/android/messaging/ui/contact/ContactListItemView.java
index 6904da6..b0e8957 100644
--- a/src/com/android/messaging/ui/contact/ContactListItemView.java
+++ b/src/com/android/messaging/ui/contact/ContactListItemView.java
@@ -53,6 +53,7 @@
     private TextView mAlphabetHeaderTextView;
     private ContactIconView mContactIconView;
     private ImageView mContactCheckmarkView;
+    private ImageView mWorkProfileIcon;
     private HostInterface mHostInterface;
     private boolean mShouldShowAlphabetHeader;
 
@@ -69,6 +70,7 @@
         mAlphabetHeaderTextView = (TextView) findViewById(R.id.alphabet_header);
         mContactIconView = (ContactIconView) findViewById(R.id.contact_icon);
         mContactCheckmarkView = (ImageView) findViewById(R.id.contact_checkmark);
+        mWorkProfileIcon = (ImageView) findViewById(R.id.work_profile_icon);
     }
 
     /**
@@ -100,11 +102,12 @@
      * @param isSingleRecipient whether this item is shown as the only line item in the single
      *        recipient drop down from the chips view. If this is the case, we always show the
      *        contact avatar even if it's not a first-level entry.
+     * @param isWorkContact whether the contact is in managed profile.
      */
     public void bind(final RecipientEntry recipientEntry, final CharSequence styledName,
             final CharSequence styledDestination, final HostInterface hostInterface,
-            final boolean isSingleRecipient) {
-        mData.bind(recipientEntry, styledName, styledDestination, isSingleRecipient);
+            final boolean isSingleRecipient, final boolean isWorkContact) {
+        mData.bind(recipientEntry, styledName, styledDestination, isSingleRecipient, isWorkContact);
         mHostInterface = hostInterface;
         mShouldShowAlphabetHeader = false;
         updateViewAppearance();
@@ -152,7 +155,11 @@
             mContactDetailsTextView.setVisibility(VISIBLE);
             mContactDetailTypeTextView.setVisibility(VISIBLE);
         }
-
+        if (mData.getIsWorkContact()) {
+            mWorkProfileIcon.setVisibility(VISIBLE);
+        } else {
+            mWorkProfileIcon.setVisibility(GONE);
+        }
         if (mShouldShowAlphabetHeader) {
             mAlphabetHeaderTextView.setVisibility(VISIBLE);
             mAlphabetHeaderTextView.setText(mData.getAlphabetHeader());
diff --git a/src/com/android/messaging/ui/contact/ContactRecipientAdapter.java b/src/com/android/messaging/ui/contact/ContactRecipientAdapter.java
index 25f422e..1d91241 100644
--- a/src/com/android/messaging/ui/contact/ContactRecipientAdapter.java
+++ b/src/com/android/messaging/ui/contact/ContactRecipientAdapter.java
@@ -22,18 +22,24 @@
 import android.text.TextUtils;
 import android.text.util.Rfc822Token;
 import android.text.util.Rfc822Tokenizer;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
 import android.widget.Filter;
+import android.widget.TextView;
 
 import com.android.ex.chips.BaseRecipientAdapter;
 import com.android.ex.chips.RecipientAlternatesAdapter;
 import com.android.ex.chips.RecipientAlternatesAdapter.RecipientMatchCallback;
 import com.android.ex.chips.RecipientEntry;
+import com.android.messaging.R;
 import com.android.messaging.util.Assert;
 import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
 import com.android.messaging.util.BugleGservices;
 import com.android.messaging.util.BugleGservicesKeys;
 import com.android.messaging.util.ContactRecipientEntryUtils;
 import com.android.messaging.util.ContactUtil;
+import com.android.messaging.util.OsUtil;
 import com.android.messaging.util.PhoneUtils;
 
 import java.text.Collator;
@@ -53,6 +59,18 @@
  * for {@link ContactRecipientAutoCompleteView}
  */
 public final class ContactRecipientAdapter extends BaseRecipientAdapter {
+    private static final int WORD_DIRECTORY_HEADER_POS_NONE = -1;
+    /**
+     * Stores the index of work directory header.
+     */
+    private int mWorkDirectoryHeaderPos = WORD_DIRECTORY_HEADER_POS_NONE;
+    private final LayoutInflater mInflater;
+
+    /**
+     * Type of directory entry.
+     */
+    private static final int ENTRY_TYPE_DIRECTORY = RecipientEntry.ENTRY_TYPE_SIZE;
+
     public ContactRecipientAdapter(final Context context,
             final ContactListItemView.HostInterface clivHost) {
         this(context, Integer.MAX_VALUE, QUERY_TYPE_PHONE, clivHost);
@@ -62,6 +80,7 @@
             final int queryMode, final ContactListItemView.HostInterface clivHost) {
         super(context, preferredMaxResultCount, queryMode);
         setPhotoManager(new ContactRecipientPhotoManager(context, clivHost));
+        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     }
 
     @Override
@@ -81,6 +100,7 @@
      * results.
      */
     public class ContactFilter extends Filter {
+
         // Used to sort filtered contacts when it has combined results from email and phone.
         private final RecipientEntryComparator mComparator = new RecipientEntryComparator();
 
@@ -95,21 +115,45 @@
          * return the merged results.
          */
         @DoesNotRunOnMainThread
-        private Pair<Cursor, Boolean> getFilteredResultsCursor(final Context context,
-                final String searchText) {
+        private CursorResult getFilteredResultsCursor(final String searchText) {
             Assert.isNotMainThread();
             if (BugleGservices.get().getBoolean(
                     BugleGservicesKeys.ALWAYS_AUTOCOMPLETE_EMAIL_ADDRESS,
                     BugleGservicesKeys.ALWAYS_AUTOCOMPLETE_EMAIL_ADDRESS_DEFAULT)) {
-                return Pair.create((Cursor) new MergeCursor(new Cursor[] {
-                        ContactUtil.filterPhones(getContext(), searchText)
-                                .performSynchronousQuery(),
-                        ContactUtil.filterEmails(getContext(), searchText)
-                                .performSynchronousQuery()
-                }), false /* the merged cursor is not sorted */);
+
+                final Cursor personalFilterPhonesCursor = ContactUtil
+                        .filterPhones(getContext(), searchText).performSynchronousQuery();
+                final Cursor personalFilterEmailsCursor = ContactUtil
+                        .filterEmails(getContext(), searchText).performSynchronousQuery();
+                final Cursor personalCursor = new MergeCursor(
+                        new Cursor[]{personalFilterEmailsCursor, personalFilterPhonesCursor});
+                final CursorResult cursorResult =
+                        new CursorResult(personalCursor, false /* sorted */);
+                if (OsUtil.isAtLeastN()) {
+                    // Including enterprise result starting from N.
+                    final Cursor enterpriseFilterPhonesCursor = ContactUtil.filterPhonesEnterprise(
+                            getContext(), searchText).performSynchronousQuery();
+                    final Cursor enterpriseFilterEmailsCursor = ContactUtil.filterEmailsEnterprise(
+                            getContext(), searchText).performSynchronousQuery();
+                    final Cursor enterpriseCursor = new MergeCursor(
+                            new Cursor[]{enterpriseFilterEmailsCursor,
+                                    enterpriseFilterPhonesCursor});
+                    cursorResult.enterpriseCursor = enterpriseCursor;
+                }
+                return cursorResult;
             } else {
-                return Pair.create(ContactUtil.filterDestination(getContext(), searchText)
-                        .performSynchronousQuery(), true);
+                final Cursor personalFilterDestinationCursor = ContactUtil
+                        .filterDestination(getContext(), searchText).performSynchronousQuery();
+                final CursorResult cursorResult = new CursorResult(personalFilterDestinationCursor,
+                        true);
+                if (OsUtil.isAtLeastN()) {
+                    // Including enterprise result starting from N.
+                    final Cursor enterpriseFilterDestinationCursor = ContactUtil
+                            .filterDestinationEnterprise(getContext(), searchText)
+                            .performSynchronousQuery();
+                    cursorResult.enterpriseCursor = enterpriseFilterDestinationCursor;
+                }
+                return cursorResult;
             }
         }
 
@@ -128,44 +172,57 @@
 
             // Query for auto-complete results, since performFiltering() is not done on the
             // main thread, perform the cursor loader queries directly.
-            final Pair<Cursor, Boolean> filteredResults = getFilteredResultsCursor(getContext(),
-                    searchText);
-            final Cursor cursor = filteredResults.first;
-            final boolean sorted = filteredResults.second;
-            if (cursor != null) {
-                try {
-                    final List<RecipientEntry> entries = new ArrayList<RecipientEntry>();
 
-                    // First check if the constraint is a valid SMS destination. If so, add the
-                    // destination as a suggestion item to the drop down.
-                    if (PhoneUtils.isValidSmsMmsDestination(searchText)) {
-                        entries.add(ContactRecipientEntryUtils
-                                .constructSendToDestinationEntry(searchText));
-                    }
+            final CursorResult cursorResult = getFilteredResultsCursor(searchText);
+            final List<RecipientEntry> entries = new ArrayList<>();
 
-                    HashSet<Long> existingContactIds = new HashSet<Long>();
-                    while (cursor.moveToNext()) {
-                        // Make sure there's only one first-level contact (i.e. contact for which
-                        // we show the avatar picture and name) for every contact id.
-                        final long contactId = cursor.getLong(ContactUtil.INDEX_CONTACT_ID);
-                        final boolean isFirstLevel = !existingContactIds.contains(contactId);
-                        if (isFirstLevel) {
-                            existingContactIds.add(contactId);
-                        }
-                        entries.add(ContactUtil.createRecipientEntryForPhoneQuery(cursor,
-                                isFirstLevel));
-                    }
+            // First check if the constraint is a valid SMS destination. If so, add the
+            // destination as a suggestion item to the drop down.
+            if (PhoneUtils.isValidSmsMmsDestination(searchText)) {
+                entries.add(ContactRecipientEntryUtils
+                        .constructSendToDestinationEntry(searchText));
+            }
 
-                    if (!sorted) {
-                        Collections.sort(entries, mComparator);
-                    }
-                    results.values = entries;
-                    results.count = 1;
-
-                } finally {
-                    cursor.close();
+            // Only show work directory header if more than one result in work directory.
+            int workDirectoryHeaderPos = WORD_DIRECTORY_HEADER_POS_NONE;
+            if (cursorResult.enterpriseCursor != null
+                    && cursorResult.enterpriseCursor.getCount() > 0) {
+                if (cursorResult.personalCursor != null) {
+                    workDirectoryHeaderPos = entries.size();
+                    workDirectoryHeaderPos += cursorResult.personalCursor.getCount();
                 }
             }
+
+            final Cursor[] cursors = new Cursor[]{cursorResult.personalCursor,
+                    cursorResult.enterpriseCursor};
+            for (Cursor cursor : cursors) {
+                if (cursor != null) {
+                    try {
+                        final List<RecipientEntry> tempEntries = new ArrayList<>();
+                        HashSet<Long> existingContactIds = new HashSet<>();
+                        while (cursor.moveToNext()) {
+                            // Make sure there's only one first-level contact (i.e. contact for
+                            // which we show the avatar picture and name) for every contact id.
+                            final long contactId = cursor.getLong(ContactUtil.INDEX_CONTACT_ID);
+                            final boolean isFirstLevel = !existingContactIds.contains(contactId);
+                            if (isFirstLevel) {
+                                existingContactIds.add(contactId);
+                            }
+                            tempEntries.add(ContactUtil.createRecipientEntryForPhoneQuery(cursor,
+                                    isFirstLevel));
+                        }
+
+                        if (!cursorResult.isSorted) {
+                            Collections.sort(tempEntries, mComparator);
+                        }
+                        entries.addAll(tempEntries);
+                    } finally {
+                        cursor.close();
+                    }
+                }
+            }
+            results.values = new ContactReceipientFilterResult(entries, workDirectoryHeaderPos);
+            results.count = 1;
             return results;
         }
 
@@ -174,16 +231,20 @@
             mCurrentConstraint = constraint;
             clearTempEntries();
 
-            if (results.values != null) {
-                @SuppressWarnings("unchecked")
-                final List<RecipientEntry> entries = (List<RecipientEntry>) results.values;
-                updateEntries(entries);
-            } else {
-                updateEntries(Collections.<RecipientEntry>emptyList());
+            final ContactReceipientFilterResult contactReceipientFilterResult
+                    = (ContactReceipientFilterResult) results.values;
+            if (contactReceipientFilterResult != null) {
+                mWorkDirectoryHeaderPos = contactReceipientFilterResult.workDirectoryPos;
+                if (contactReceipientFilterResult.recipientEntries != null) {
+                    updateEntries(contactReceipientFilterResult.recipientEntries);
+                } else {
+                    updateEntries(Collections.<RecipientEntry>emptyList());
+                }
             }
         }
 
         private class RecipientEntryComparator implements Comparator<RecipientEntry> {
+
             private final Collator mCollator;
 
             public RecipientEntryComparator() {
@@ -237,6 +298,38 @@
                 }
             }
         }
+
+        private class CursorResult {
+
+            public final Cursor personalCursor;
+
+            public Cursor enterpriseCursor;
+
+            public final boolean isSorted;
+
+            public CursorResult(Cursor personalCursor, boolean isSorted) {
+                this.personalCursor = personalCursor;
+                this.isSorted = isSorted;
+            }
+        }
+
+        private class ContactReceipientFilterResult {
+            /**
+             * Recipient entries in all directories.
+             */
+            public final List<RecipientEntry> recipientEntries;
+
+            /**
+             * Index of row that showing work directory header.
+             */
+            public final int workDirectoryPos;
+
+            public ContactReceipientFilterResult(List<RecipientEntry> recipientEntries,
+                    int workDirectoryPos) {
+                this.recipientEntries = recipientEntries;
+                this.workDirectoryPos = workDirectoryPos;
+            }
+        }
     }
 
     /**
@@ -283,4 +376,81 @@
         // report matches
         callback.matchesFound(recipientEntries);
     }
+
+    /**
+     * We handle directory header here and then delegate the work of creating recipient views to
+     * the {@link BaseRecipientAdapter}. Please notice that we need to fix the position
+     * before passing to {@link BaseRecipientAdapter} because it is not aware of the existence of
+     * directory headers.
+     */
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        TextView textView;
+        if (isDirectoryEntry(position)) {
+            if (convertView == null) {
+                textView = (TextView) mInflater.inflate(R.layout.work_directory_header, parent,
+                        false);
+            } else {
+                textView = (TextView) convertView;
+            }
+            return textView;
+        }
+        return super.getView(fixPosition(position), convertView, parent);
+    }
+
+    @Override
+    public RecipientEntry getItem(int position) {
+        if (isDirectoryEntry(position)) {
+            return null;
+        }
+        return super.getItem(fixPosition(position));
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return RecipientEntry.ENTRY_TYPE_SIZE + 1;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        if (isDirectoryEntry(position)) {
+            return ENTRY_TYPE_DIRECTORY;
+        }
+        return super.getItemViewType(fixPosition(position));
+    }
+
+    @Override
+    public boolean isEnabled(int position) {
+        if (isDirectoryEntry(position)) {
+            return false;
+        }
+        return super.isEnabled(fixPosition(position));
+    }
+
+    @Override
+    public int getCount() {
+        return super.getCount() + ((hasWorkDirectoryHeader()) ? 1 : 0);
+    }
+
+    private boolean isDirectoryEntry(int position) {
+        return position == mWorkDirectoryHeaderPos;
+    }
+
+    /**
+     * @return the position of items without counting directory headers.
+     */
+    private int fixPosition(int position) {
+        if (hasWorkDirectoryHeader()) {
+            Assert.isTrue(position != mWorkDirectoryHeaderPos);
+            if (position > mWorkDirectoryHeaderPos) {
+                return position - 1;
+            }
+        }
+        return position;
+    }
+
+    private boolean hasWorkDirectoryHeader() {
+        return mWorkDirectoryHeaderPos != WORD_DIRECTORY_HEADER_POS_NONE;
+    }
+
 }
diff --git a/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java b/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java
index ef7fcef..83b7be9 100644
--- a/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java
+++ b/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java
@@ -34,6 +34,7 @@
 import com.android.messaging.util.ContentType;
 import com.android.messaging.util.LogUtil;
 import com.android.messaging.util.MediaMetadataRetrieverWrapper;
+import com.android.messaging.util.FileUtil;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -158,8 +159,12 @@
     }
 
     private void addSharedImagePartToDraft(final String contentType, final Uri imageUri) {
-        mDraftMessage.addPart(PendingAttachmentData.createPendingAttachmentData(contentType,
-                imageUri));
+        if (FileUtil.isInPrivateDir(imageUri)) {
+            Assert.fail("Cannot send private file " + imageUri.toString());
+        } else {
+            mDraftMessage.addPart(PendingAttachmentData.createPendingAttachmentData(contentType,
+                    imageUri));
+        }
     }
 
     @Override
diff --git a/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java b/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java
index dff59cf..bb267da 100644
--- a/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java
+++ b/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java
@@ -28,9 +28,6 @@
 import com.android.messaging.util.FileUtil;
 import com.android.messaging.util.ImageUtils;
 import com.android.messaging.util.SafeAsyncTask;
-import com.android.messaging.util.UriUtil;
-
-import java.io.File;
 
 /**
  * Wraps around the functionalities to allow the user to pick images from the document
@@ -116,8 +113,7 @@
         new SafeAsyncTask<Void, Void, String>() {
             @Override
             protected String doInBackgroundTimed(final Void... params) {
-                if (UriUtil.isFileUri(documentUri) &&
-                        FileUtil.isInDataDir(new File(documentUri.getPath()))) {
+                if (FileUtil.isInPrivateDir(documentUri)) {
                     // hacker sending private app data. Bail out
                     if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.ERROR)) {
                         LogUtil.e(LogUtil.BUGLE_TAG, "Aborting attach of private app data ("
diff --git a/src/com/android/messaging/util/ContactUtil.java b/src/com/android/messaging/util/ContactUtil.java
index 8555889..94af3ba 100644
--- a/src/com/android/messaging/util/ContactUtil.java
+++ b/src/com/android/messaging/util/ContactUtil.java
@@ -274,6 +274,18 @@
     }
 
     /**
+     * Get a list of destinations (phone, email) matching the partial destination in work profile.
+     */
+    public static CursorQueryData filterDestinationEnterprise(final Context context,
+            final String destination) {
+        if (shouldFilterForEmail(destination)) {
+            return ContactUtil.filterEmailsEnterprise(context, destination);
+        } else {
+            return ContactUtil.filterPhonesEnterprise(context, destination);
+        }
+    }
+
+    /**
      * Get a list of phones matching a search criteria. The search may be on contact name or
      * phone number. In case search is on contact name, all matching contact's phone number
      * will be returned.
@@ -282,19 +294,29 @@
      */
     @VisibleForTesting
     public static CursorQueryData filterPhones(final Context context, final String query) {
+        return filterPhonesInternal(context, Phone.CONTENT_FILTER_URI, query, Directory.DEFAULT);
+    }
+
+    /**
+     * Similar to {@link #filterPhones(Context, String)}, but search in work profile instead.
+     */
+    public static CursorQueryData filterPhonesEnterprise(final Context context,
+            final String query) {
+        return filterPhonesInternal(context, Phone.ENTERPRISE_CONTENT_FILTER_URI, query,
+                Directory.ENTERPRISE_DEFAULT);
+    }
+
+    private static CursorQueryData filterPhonesInternal(final Context context,
+            final Uri phoneFilterBaseUri, final String query, final long directoryId) {
         if (!ContactUtil.hasReadContactsPermission()) {
             return CursorQueryData.getEmptyQueryData();
         }
-
-        final Uri uri = Phone.CONTENT_FILTER_URI.buildUpon()
-                .appendPath(query).appendQueryParameter(
-                        ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
-                        .build();
-
-        return new CursorQueryData(context, uri, PhoneQuery.PROJECTION, null, null,
+        Uri phoneFilterUri = buildDirectorySearchUri(phoneFilterBaseUri, query, directoryId);
+        return new CursorQueryData(context,
+                phoneFilterUri,
+                PhoneQuery.PROJECTION, null, null,
                 PhoneQuery.SORT_KEY);
     }
-
     /**
      * Lookup a phone based on a phone number. Supplied phone should be a relatively complete
      * phone number for this to succeed. PhoneLookup URI will apply some smartness to do a
@@ -336,17 +358,29 @@
      */
     @VisibleForTesting
     public static CursorQueryData filterEmails(final Context context, final String query) {
+        return filterEmailsInternal(context, Email.CONTENT_FILTER_URI, query, Directory.DEFAULT);
+    }
+
+    /**
+     * Similar to {@link #filterEmails(Context, String)}, but search in work profile instead.
+     */
+    public static CursorQueryData filterEmailsEnterprise(final Context context,
+            final String query) {
+        return filterEmailsInternal(context, Email.ENTERPRISE_CONTENT_FILTER_URI, query,
+                Directory.ENTERPRISE_DEFAULT);
+    }
+
+    private static CursorQueryData filterEmailsInternal(final Context context,
+            final Uri filterEmailsBaseUri, final String query, final long directoryId) {
         if (!ContactUtil.hasReadContactsPermission()) {
             return CursorQueryData.getEmptyQueryData();
         }
-
-        final Uri uri = Email.CONTENT_FILTER_URI.buildUpon()
-                .appendPath(query).appendQueryParameter(
-                        ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
-                        .build();
-
-        return new CursorQueryData(context, uri, EmailQuery.PROJECTION, null, null,
-                EmailQuery.SORT_KEY);
+        final Uri filterEmailsUri = buildDirectorySearchUri(filterEmailsBaseUri, query,
+                directoryId);
+        return new CursorQueryData(context,
+                filterEmailsUri,
+                PhoneQuery.PROJECTION, null, null,
+                PhoneQuery.SORT_KEY);
     }
 
     /**
@@ -485,25 +519,15 @@
      * Returns if a given contact id belongs to managed profile.
      */
     public static boolean isEnterpriseContactId(final long contactId) {
-        return isWorkProfileSupported()
-                && ContactsContract.Contacts.isEnterpriseContactId(contactId);
-    }
-
-    /**
-     * Returns if managed profile is supported.
-     */
-    public static boolean isWorkProfileSupported() {
-        final PackageManager pm = Factory.get().getApplicationContext().getPackageManager();
-        return pm.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS);
+        return OsUtil.isAtLeastL() && ContactsContract.Contacts.isEnterpriseContactId(contactId);
     }
 
     /**
      * Returns Email lookup uri that will query both primary and corp profile
      */
     private static Uri getEmailContentLookupUri() {
-        if (isWorkProfileSupported() && OsUtil.isAtLeastM()) {
-            // TODO: use Email.ENTERPRISE_CONTENT_LOOKUP_URI, which will be available in M SDK API
-            return Uri.parse("content://com.android.contacts/data/emails/lookup_enterprise");
+        if (OsUtil.isAtLeastM()) {
+            return Email.ENTERPRISE_CONTENT_LOOKUP_URI;
         }
         return Email.CONTENT_LOOKUP_URI;
     }
@@ -512,8 +536,7 @@
      * Returns PhoneLookup URI.
      */
     public static Uri getPhoneLookupUri() {
-        // Apply it to M only
-        if (isWorkProfileSupported() && OsUtil.isAtLeastM()) {
+        if (OsUtil.isAtLeastM()) {
             return PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI;
         }
         return PhoneLookup.CONTENT_FILTER_URI;
@@ -522,4 +545,12 @@
     public static boolean hasReadContactsPermission() {
         return OsUtil.hasPermission(Manifest.permission.READ_CONTACTS);
     }
+
+    private static Uri buildDirectorySearchUri(final Uri uri, final String query,
+            final long directoryId) {
+        return uri.buildUpon()
+                .appendPath(query).appendQueryParameter(
+                        ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId))
+                .build();
+    }
 }
diff --git a/src/com/android/messaging/util/FileUtil.java b/src/com/android/messaging/util/FileUtil.java
index b147b25..e35e79b 100644
--- a/src/com/android/messaging/util/FileUtil.java
+++ b/src/com/android/messaging/util/FileUtil.java
@@ -16,8 +16,11 @@
 
 package com.android.messaging.util;
 
+import android.content.ContentResolver;
 import android.content.Context;
+import android.net.Uri;
 import android.os.Environment;
+import android.text.TextUtils;
 import android.webkit.MimeTypeMap;
 
 import com.android.messaging.Factory;
@@ -117,11 +120,19 @@
         }
     }
 
+    private static boolean isFileUri(final Uri uri) {
+        return TextUtils.equals(uri.getScheme(), ContentResolver.SCHEME_FILE);
+    }
+
     // Checks if the file is in /data, and don't allow any app to send personal information.
     // We're told it's possible to create world readable hardlinks to other apps private data
-    // so we ban all /data file uris. b/28793303
-    public static boolean isInDataDir(File file) {
-        return isSameOrSubDirectory(Environment.getDataDirectory(), file);
+    // so we ban all /data file uris.
+    public static boolean isInPrivateDir(Uri uri) {
+        if (!isFileUri(uri)) {
+            return false;
+        }
+        final File file = new File(uri.getPath());
+        return FileUtil.isSameOrSubDirectory(Environment.getDataDirectory(), file);
     }
 
     /**
diff --git a/src/com/android/messaging/util/OsUtil.java b/src/com/android/messaging/util/OsUtil.java
index e45a63c..4890d08 100644
--- a/src/com/android/messaging/util/OsUtil.java
+++ b/src/com/android/messaging/util/OsUtil.java
@@ -22,6 +22,7 @@
 import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.support.v4.os.BuildCompat;
 
 import com.android.messaging.Factory;
 
@@ -41,6 +42,7 @@
     private static boolean sIsAtLeastL;
     private static boolean sIsAtLeastL_MR1;
     private static boolean sIsAtLeastM;
+    private static boolean sIsAtLeastN;
 
     private static Boolean sIsSecondaryUser = null;
 
@@ -54,6 +56,7 @@
         sIsAtLeastL = v >= android.os.Build.VERSION_CODES.LOLLIPOP;
         sIsAtLeastL_MR1 = v >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
         sIsAtLeastM = v >= android.os.Build.VERSION_CODES.M;
+        sIsAtLeastN = BuildCompat.isAtLeastN();
     }
 
     /**
@@ -121,6 +124,14 @@
     }
 
     /**
+     * @return True if the version of Android that we're running on is at least N
+     *  (API level 24).
+     */
+    public static boolean isAtLeastN() {
+        return sIsAtLeastN;
+    }
+
+    /**
      * @return The Android API version of the OS that we're currently running on.
      */
     public static int getApiVersion() {
diff --git a/tests/Android.mk b/tests/Android.mk
index f3f4752..3bfdb9a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -24,8 +24,6 @@
 
 LOCAL_INSTRUMENTATION_FOR := messaging
 
-LOCAL_JACK_ENABLED := disabled
-
 # Matching ../Android.mk
 LOCAL_SDK_VERSION := current
 
diff --git a/version.mk b/version.mk
index 7407993..6568cc8 100644
--- a/version.mk
+++ b/version.mk
@@ -100,10 +100,12 @@
 version_code_package := $(base_version_major)$(base_version_minor)$(base_version_build)$(base_version_buildtype)$(base_version_arch)$(base_version_density)
 
 # The version name scheme for the package apk is:
+# - For platform builds:     M.m.bbb
 # - For eng build (t=1):     M.m.bbb eng.$(USER)-hh
 # - For build server (t=0):  M.m.bbb (nnnnnn-hh)
 #       where nnnnnn is the build number from the build server (no zero-padding)
 # On eng builds, the BUILD_NUMBER has the user and timestamp inline
+ifdef TARGET_BUILD_APPS
 ifneq "" "$(filter eng.%,$(BUILD_NUMBER))"
   git_hash := $(shell git --git-dir $(LOCAL_PATH)/.git log -n 1 --pretty=format:%h)
   date_string := $(shell date +%m%d%y_%H%M%S)
@@ -111,6 +113,9 @@
 else
   version_name_package := $(base_version_major).$(base_version_minor).$(base_version_build) ($(BUILD_NUMBER)-$(base_version_arch)$(base_version_density))
 endif
+else # !TARGET_BUILD_APPS
+  version_name_package := $(base_version_major).$(base_version_minor).$(base_version_build)
+endif
 
 # Cleanup the locals
 base_version_major :=