Add Feature Flag support.

Bug: 35923154
Change-Id: I34dd956e8309c779e7a3f35eadc3d84132b970ce
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index cde116b..3897dc3 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -32,18 +32,17 @@
 
 import com.android.documentsui.AbstractActionHandler.CommonAddons;
 import com.android.documentsui.LoadDocStackTask.LoadDocStackCallback;
-import com.android.documentsui.archives.ArchivesProvider;
 import com.android.documentsui.base.BooleanConsumer;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.DocumentStack;
 import com.android.documentsui.base.Lookup;
+import com.android.documentsui.base.Providers;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
 import com.android.documentsui.base.State;
-import com.android.documentsui.dirlist.AnimationView.AnimationType;
 import com.android.documentsui.dirlist.AnimationView;
+import com.android.documentsui.dirlist.AnimationView.AnimationType;
 import com.android.documentsui.dirlist.DocumentDetails;
-import com.android.documentsui.dirlist.DocumentsAdapter;
 import com.android.documentsui.dirlist.FocusHandler;
 import com.android.documentsui.dirlist.Model;
 import com.android.documentsui.files.LauncherActivity;
@@ -58,7 +57,6 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
-import java.util.function.Consumer;
 
 import javax.annotation.Nullable;
 
@@ -132,8 +130,13 @@
 
     @Override
     public void refreshDocument(DocumentInfo doc, BooleanConsumer callback) {
-        RefreshTask task = new RefreshTask(mState, doc, REFRESH_SPINNER_TIMEOUT,
-                mActivity.getApplicationContext(), mActivity::isDestroyed,
+        RefreshTask task = new RefreshTask(
+                mInjector.features,
+                mState,
+                doc,
+                REFRESH_SPINNER_TIMEOUT,
+                mActivity.getApplicationContext(),
+                mActivity::isDestroyed,
                 callback);
         task.executeOnExecutor(mExecutors.lookup(doc == null ? null : doc.authority));
     }
@@ -345,7 +348,7 @@
 
     protected final boolean launchToDocument(Uri uri) {
         // We don't support launching to a document in an archive.
-        if (!ArchivesProvider.AUTHORITY.equals(uri.getAuthority())) {
+        if (!Providers.isArchiveUri(uri)) {
             loadDocument(uri, this::onStackLoaded);
             return true;
         }
diff --git a/src/com/android/documentsui/DirectoryLoader.java b/src/com/android/documentsui/DirectoryLoader.java
index 5c93f76..0a4dff1 100644
--- a/src/com/android/documentsui/DirectoryLoader.java
+++ b/src/com/android/documentsui/DirectoryLoader.java
@@ -16,10 +16,13 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.base.Shared.VERBOSE;
+
 import android.content.AsyncTaskLoader;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
@@ -36,7 +39,6 @@
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.FilteringCursorWrapper;
 import com.android.documentsui.base.RootInfo;
-import com.android.documentsui.base.Shared;
 import com.android.documentsui.roots.RootCursorWrapper;
 import com.android.documentsui.sorting.SortModel;
 
@@ -100,7 +102,8 @@
             }
             result.client = client;
 
-            if (Shared.ENABLE_OMC_API_FEATURES) {
+            Resources resources = getContext().getResources();
+            if (resources.getBoolean(R.bool.feature_content_paging)) {
                 Bundle queryArgs = new Bundle();
                 mModel.addQuerySortArgs(queryArgs);
 
@@ -122,12 +125,19 @@
 
             cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
 
-            if (mSearchMode && !Shared.ENABLE_OMC_API_FEATURES) {
+            if (mSearchMode && !resources.getBoolean(R.bool.feature_folders_in_search_results)) {
                 // There is no findDocumentPath API. Enable filtering on folders in search mode.
                 cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
             }
 
-            cursor = mModel.sortCursor(cursor);
+            // TODO: When API tweaks have landed, use ContentResolver.EXTRA_HONORED_ARGS
+            // instead of checking directly for ContentResolver.QUERY_ARG_SORT_COLUMNS (won't work)
+            if (resources.getBoolean(R.bool.feature_content_paging)
+                        && cursor.getExtras().containsKey(ContentResolver.QUERY_ARG_SORT_COLUMNS)) {
+                if (VERBOSE) Log.d(TAG, "Skipping sort of pre-sorted cursor. Booya!");
+            } else {
+                cursor = mModel.sortCursor(cursor);
+            }
             result.cursor = cursor;
         } catch (Exception e) {
             Log.w(TAG, "Failed to query", e);
diff --git a/src/com/android/documentsui/FocusManager.java b/src/com/android/documentsui/FocusManager.java
index ecc4251..8126e4d 100644
--- a/src/com/android/documentsui/FocusManager.java
+++ b/src/com/android/documentsui/FocusManager.java
@@ -18,6 +18,7 @@
 
 import static com.android.documentsui.base.DocumentInfo.getCursorString;
 import static com.android.documentsui.base.Shared.DEBUG;
+import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.ColorRes;
 import android.annotation.Nullable;
@@ -41,8 +42,8 @@
 
 import com.android.documentsui.base.EventListener;
 import com.android.documentsui.base.Events;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.Procedure;
-import com.android.documentsui.base.Shared;
 import com.android.documentsui.dirlist.DocumentHolder;
 import com.android.documentsui.dirlist.DocumentsAdapter;
 import com.android.documentsui.dirlist.FocusHandler;
@@ -60,6 +61,7 @@
 
     private final ContentScope mScope = new ContentScope();
 
+    private final Features mFeatures;
     private final SelectionManager mSelectionMgr;
     private final DrawerController mDrawer;
     private final Procedure mRootsFocuser;
@@ -68,11 +70,13 @@
     private boolean mNavDrawerHasFocus;
 
     public FocusManager(
+            Features features,
             SelectionManager selectionMgr,
             DrawerController drawer,
             Procedure rootsFocuser,
             @ColorRes int color) {
 
+        mFeatures = checkNotNull(features);
         mSelectionMgr = selectionMgr;
         mDrawer = drawer;
         mRootsFocuser = rootsFocuser;
@@ -84,7 +88,7 @@
     public boolean advanceFocusArea() {
         // This should only be called in pre-O devices.
         // O has built-in keyboard navigation support.
-        assert(!Shared.ENABLE_OMC_API_FEATURES);
+        assert(!mFeatures.isSystemKeyboardNavigationEnabled());
         boolean focusChanged = false;
         if (mNavDrawerHasFocus) {
             mDrawer.setOpen(false);
diff --git a/src/com/android/documentsui/Injector.java b/src/com/android/documentsui/Injector.java
index 7474cbe..0a3f5bb 100644
--- a/src/com/android/documentsui/Injector.java
+++ b/src/com/android/documentsui/Injector.java
@@ -25,6 +25,7 @@
 
 import com.android.documentsui.MenuManager.SelectionDetails;
 import com.android.documentsui.base.EventHandler;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.dirlist.DocumentsAdapter;
 import com.android.documentsui.dirlist.Model;
 import com.android.documentsui.prefs.ScopedPreferences;
@@ -41,6 +42,7 @@
  */
 public class Injector<T extends ActionHandler> {
 
+    public final Features features;
     public final ActivityConfig config;
     public final ScopedPreferences prefs;
     public final MessageBuilder messages;
@@ -60,21 +62,24 @@
     @ContentScoped
     public SelectionManager selectionMgr;
 
-    private final Model mModel = new Model();
-    private DocumentsAdapter mAdapter;
+    private final Model mModel;
 
     // must be initialized before calling super.onCreate because prefs
     // are used in State initialization.
     public Injector(
+            Features features,
             ActivityConfig config,
             ScopedPreferences prefs,
             MessageBuilder messages,
             DialogController dialogs) {
 
+        this.features = features;
         this.config = config;
         this.prefs = prefs;
         this.messages = messages;
         this.dialogs = dialogs;
+
+        mModel = new Model(this.features);
     }
 
     public Model getModel() {
diff --git a/src/com/android/documentsui/LoadDocStackTask.java b/src/com/android/documentsui/LoadDocStackTask.java
index 712f5e1..7d2eb38 100644
--- a/src/com/android/documentsui/LoadDocStackTask.java
+++ b/src/com/android/documentsui/LoadDocStackTask.java
@@ -25,9 +25,9 @@
 
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.DocumentStack;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.PairedTask;
 import com.android.documentsui.base.RootInfo;
-import com.android.documentsui.base.Shared;
 import com.android.documentsui.roots.RootsAccess;
 
 import java.util.List;
@@ -61,7 +61,8 @@
 
     @Override
     public @Nullable DocumentStack run(Uri... uris) {
-        if (Shared.ENABLE_OMC_API_FEATURES && mDocs.isDocumentUri(uris[0])) {
+        // assert(Features.OMC_RUNTIME);
+        if (mDocs.isDocumentUri(uris[0])) {
             final Uri docUri;
             if (DocumentsContract.isTreeUri(uris[0])) {
                 // Reconstruct tree URI into a plain document URI so that we can get the full path
diff --git a/src/com/android/documentsui/RecentsLoader.java b/src/com/android/documentsui/RecentsLoader.java
index ead5e8f..e5827b3 100644
--- a/src/com/android/documentsui/RecentsLoader.java
+++ b/src/com/android/documentsui/RecentsLoader.java
@@ -33,9 +33,9 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.FilteringCursorWrapper;
 import com.android.documentsui.base.RootInfo;
-import com.android.documentsui.base.Shared;
 import com.android.documentsui.base.State;
 import com.android.documentsui.roots.RootCursorWrapper;
 import com.android.documentsui.roots.RootsAccess;
@@ -83,6 +83,7 @@
 
     private final RootsAccess mRoots;
     private final State mState;
+    private final Features mFeatures;
 
     @GuardedBy("mTasks")
     /** A authority -> RecentsTask map */
@@ -93,10 +94,11 @@
 
     private DirectoryResult mResult;
 
-    public RecentsLoader(Context context, RootsAccess roots, State state) {
+    public RecentsLoader(Context context, RootsAccess roots, State state, Features features) {
         super(context);
         mRoots = roots;
         mState = state;
+        mFeatures = features;
 
         // Keep clients around on high-RAM devices, since we'd be spinning them
         // up moments later to fetch thumbnails anyway.
@@ -330,7 +332,7 @@
                     final Uri uri =
                             DocumentsContract.buildRecentDocumentsUri(authority, rootIds.get(i));
                     try {
-                        if (Shared.ENABLE_OMC_API_FEATURES) {
+                        if (mFeatures.isContentPagingEnabled()) {
                             final Bundle queryArgs = new Bundle();
                             mState.sortModel.addQuerySortArgs(queryArgs);
                             res[i] = client.query(uri, null, queryArgs, null);
diff --git a/src/com/android/documentsui/RefreshTask.java b/src/com/android/documentsui/RefreshTask.java
index ef6a16e..deaf0ca 100644
--- a/src/com/android/documentsui/RefreshTask.java
+++ b/src/com/android/documentsui/RefreshTask.java
@@ -19,8 +19,6 @@
 import static com.android.documentsui.base.Shared.DEBUG;
 
 import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.Fragment;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -32,7 +30,7 @@
 import com.android.documentsui.base.BooleanConsumer;
 import com.android.documentsui.base.CheckedTask;
 import com.android.documentsui.base.DocumentInfo;
-import com.android.documentsui.base.Shared;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.State;
 
 /**
@@ -45,17 +43,20 @@
     private final static String TAG = "RefreshTask";
 
     private final @ApplicationScope Context mContext;
+    private final Features mFeatures;
     private final State mState;
     private final DocumentInfo mDoc;
     private final BooleanConsumer mCallback;
     private final CancellationSignal mSignal;
 
-    public RefreshTask(State state, DocumentInfo doc, long timeout,
+
+    public RefreshTask(Features features, State state, DocumentInfo doc, long timeout,
             @ApplicationScope Context context, Check check, BooleanConsumer callback) {
         super(check);
+        mFeatures = features;
+        mState = state;
         mDoc = doc;
         mContext = context;
-        mState = state;
         mCallback = callback;
         mSignal = new CancellationSignal();
         setTimeout(timeout);
@@ -82,7 +83,7 @@
         // supports it, the ContentProvider will automatically send a content updated notification
         // and we will update accordingly. Else, we just tell the callback that Refresh is not
         // supported.
-        if (!Shared.ENABLE_OMC_API_FEATURES) {
+        if (!mFeatures.isContentRefreshEnabled()) {
             Log.w(TAG, "Ignoring attempt to call Refresh on an older Android platform.");
             return false;
         }
diff --git a/src/com/android/documentsui/SharedInputHandler.java b/src/com/android/documentsui/SharedInputHandler.java
index c5417ea..3678059 100644
--- a/src/com/android/documentsui/SharedInputHandler.java
+++ b/src/com/android/documentsui/SharedInputHandler.java
@@ -18,17 +18,19 @@
 import android.view.KeyEvent;
 
 import com.android.documentsui.base.Events;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.Procedure;
-import com.android.documentsui.base.Shared;
 
 public class SharedInputHandler {
 
     private final FocusManager mFocusManager;
-    private Procedure mDirPopper;
+    private final Procedure mDirPopper;
+    private final Features mFeatures;
 
-    public SharedInputHandler(FocusManager focusManager, Procedure dirPopper) {
+    public SharedInputHandler(FocusManager focusManager, Procedure dirPopper, Features features) {
         mFocusManager = focusManager;
         mDirPopper = dirPopper;
+        mFeatures = features;
     }
 
     public boolean onKeyDown(int keyCode, KeyEvent event) {
@@ -38,7 +40,8 @@
             // which is probably what the user is trying to do.
             mFocusManager.focusDirectoryList();
             return true;
-        } else if (keyCode == KeyEvent.KEYCODE_TAB && !Shared.ENABLE_OMC_API_FEATURES) {
+        } else if (keyCode == KeyEvent.KEYCODE_TAB
+                && !mFeatures.isSystemKeyboardNavigationEnabled()) {
             // Tab toggles focus on the navigation drawer.
             // This should only be called in pre-O devices, since O has built-in keyboard navigation
             // support.
diff --git a/src/com/android/documentsui/base/DebugFlags.java b/src/com/android/documentsui/base/DebugFlags.java
index e131d8e..2298e76 100644
--- a/src/com/android/documentsui/base/DebugFlags.java
+++ b/src/com/android/documentsui/base/DebugFlags.java
@@ -65,7 +65,6 @@
     }
 
     public static boolean addForcedPagingArgs(Bundle queryArgs) {
-        assert(Shared.ENABLE_OMC_API_FEATURES);
         boolean flagsAdded = false;
         if (sForcedPageOffset >= 0) {
             queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, sForcedPageOffset);
diff --git a/src/com/android/documentsui/base/DocumentFilters.java b/src/com/android/documentsui/base/DocumentFilters.java
new file mode 100644
index 0000000..de8085b
--- /dev/null
+++ b/src/com/android/documentsui/base/DocumentFilters.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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 ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.documentsui.base;
+
+import static com.android.documentsui.base.DocumentInfo.getCursorInt;
+import static com.android.documentsui.base.DocumentInfo.getCursorString;
+
+import android.database.Cursor;
+import android.provider.DocumentsContract.Document;
+
+import com.android.documentsui.archives.ArchivesProvider;
+import com.android.documentsui.roots.RootCursorWrapper;
+
+import java.util.function.Predicate;
+
+/**
+ * Predicates for matching certain types of document records
+ * in {@link Cursor}s.
+ */
+public final class DocumentFilters {
+
+    public static final Predicate<Cursor> ANY = (Cursor c) -> { return true; };
+    public static final Predicate<Cursor> VIRTUAL  = DocumentFilters::isVirtual;
+    private static final Predicate<Cursor> O_SHARABLE = DocumentFilters::isSharableInO;
+    private static final Predicate<Cursor> PREO_SHARABLE = DocumentFilters::isSharablePreO;
+
+    public static Predicate<Cursor> sharable(Features features) {
+        return features.isVirtualFilesSharingEnabled()
+                ? DocumentFilters.O_SHARABLE
+                : DocumentFilters.PREO_SHARABLE;
+    }
+
+    private static boolean isSharableInO(Cursor c) {
+        int flags = getCursorInt(c, Document.COLUMN_FLAGS);
+        String authority = getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY);
+
+        return (flags & Document.FLAG_PARTIAL) == 0
+                && !ArchivesProvider.AUTHORITY.equals(authority);
+    }
+
+    /**
+     * Filter that passes (returns true) for all files which can be shared in O.
+     */
+    private static boolean isSharablePreO(Cursor c) {
+        int flags = getCursorInt(c, Document.COLUMN_FLAGS);
+        String authority = getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY);
+
+        return (flags & Document.FLAG_PARTIAL) == 0
+                && (flags & Document.FLAG_VIRTUAL_DOCUMENT) == 0
+                && !ArchivesProvider.AUTHORITY.equals(authority);
+    }
+
+    /**
+     * Filter that passes (returns true) only virtual documents.
+     */
+    private static final boolean isVirtual(Cursor c) {
+        int flags = getCursorInt(c, Document.COLUMN_FLAGS);
+        return (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0;
+    }
+}
diff --git a/src/com/android/documentsui/base/Features.java b/src/com/android/documentsui/base/Features.java
new file mode 100644
index 0000000..836d03a
--- /dev/null
+++ b/src/com/android/documentsui/base/Features.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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 ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.documentsui.base;
+
+import android.content.res.Resources;
+
+import com.android.documentsui.R;
+
+/**
+ * Provides access to feature flags configured in config.xml.
+ */
+public interface Features {
+
+    // technically we want to check >= O, but we'd need to patch back the O version code :|
+    public static final boolean OMC_RUNTIME =
+            android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.N_MR1;
+
+    boolean isArchiveCreationEnabled();
+    boolean isRemoteActionsEnabled();
+    boolean isContentPagingEnabled();
+    boolean isFoldersInSearchResultsEnabled();
+    boolean isSystemKeyboardNavigationEnabled();
+    boolean isLaunchToDocumentEnabled();
+    boolean isVirtualFilesSharingEnabled();
+    boolean isContentRefreshEnabled();
+
+    public static Features create(Resources resources) {
+        return new RuntimeFeatures(resources);
+    }
+
+    final class RuntimeFeatures implements Features {
+        private final Resources mRes;
+
+        public RuntimeFeatures(Resources resources) {
+            mRes = resources;
+        }
+
+        @Override
+        public boolean isArchiveCreationEnabled() {
+            return mRes.getBoolean(R.bool.feature_archive_creation);
+        }
+
+        @Override
+        public boolean isRemoteActionsEnabled() {
+            return mRes.getBoolean(R.bool.feature_remote_actions);
+        }
+
+        @Override
+        public boolean isContentPagingEnabled() {
+            return mRes.getBoolean(R.bool.feature_content_paging);
+        }
+
+        @Override
+        public boolean isFoldersInSearchResultsEnabled() {
+            return mRes.getBoolean(R.bool.feature_folders_in_search_results);
+        }
+
+        @Override
+        public boolean isSystemKeyboardNavigationEnabled() {
+            return mRes.getBoolean(R.bool.feature_system_keyboard_navigation);
+        }
+
+        @Override
+        public boolean isLaunchToDocumentEnabled() {
+            return mRes.getBoolean(R.bool.feature_launch_to_document);
+        }
+
+        @Override
+        public boolean isContentRefreshEnabled() {
+            return mRes.getBoolean(R.bool.feature_content_refresh);
+        }
+
+        @Override
+        public boolean isVirtualFilesSharingEnabled() {
+            return mRes.getBoolean(R.bool.feature_virtual_files_sharing);
+        }
+    }
+}
diff --git a/src/com/android/documentsui/base/Providers.java b/src/com/android/documentsui/base/Providers.java
index 561c442..9f825dc 100644
--- a/src/com/android/documentsui/base/Providers.java
+++ b/src/com/android/documentsui/base/Providers.java
@@ -15,6 +15,10 @@
  */
 package com.android.documentsui.base;
 
+import android.net.Uri;
+
+import com.android.documentsui.archives.ArchivesProvider;
+
 /**
  * Details about various system providers. These all need to be in sync with the
  * resources in their respective packages.
@@ -34,4 +38,8 @@
     public static final String ROOT_ID_AUDIO = "audio_root";
 
     public static final String AUTHORITY_MTP = "com.android.mtp.documents";
+
+    public static boolean isArchiveUri(Uri uri) {
+        return uri != null && ArchivesProvider.AUTHORITY.equals(uri.getAuthority());
+    }
 }
diff --git a/src/com/android/documentsui/base/Shared.java b/src/com/android/documentsui/base/Shared.java
index fa228d1..7616edb 100644
--- a/src/com/android/documentsui/base/Shared.java
+++ b/src/com/android/documentsui/base/Shared.java
@@ -51,8 +51,6 @@
     public static final boolean DEBUG = true;
     public static final boolean VERBOSE = DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
 
-    public static final boolean ENABLE_OMC_API_FEATURES = true;
-
     /** Intent action name to pick a copy destination. */
     public static final String ACTION_PICK_COPY_DESTINATION =
             "com.android.documentsui.PICK_COPY_DESTINATION";
diff --git a/src/com/android/documentsui/clipping/DocumentClipper.java b/src/com/android/documentsui/clipping/DocumentClipper.java
index 2ec65d1..d0dfc7c 100644
--- a/src/com/android/documentsui/clipping/DocumentClipper.java
+++ b/src/com/android/documentsui/clipping/DocumentClipper.java
@@ -29,6 +29,7 @@
 
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.DocumentStack;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
 import com.android.documentsui.selection.Selection;
@@ -337,7 +338,8 @@
     private static ClipData createClipData(
             ClipDescription description, ArrayList<ClipData.Item> clipItems) {
 
-        if (Shared.ENABLE_OMC_API_FEATURES) {
+        // technically we want to check >= O, but we'd need to patch back the O version code :|
+        if (Features.OMC_RUNTIME) {
             return new ClipData(description, clipItems);
         }
 
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 908205c..57facaf3 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -86,6 +86,7 @@
 import com.android.documentsui.base.EventListener;
 import com.android.documentsui.base.Events.InputEvent;
 import com.android.documentsui.base.Events.MotionInputEvent;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
 import com.android.documentsui.base.State;
@@ -111,7 +112,6 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.List;
 
 import javax.annotation.Nullable;
@@ -1176,6 +1176,11 @@
     private final class AdapterEnvironment implements DocumentsAdapter.Environment {
 
         @Override
+        public Features getFeatures() {
+            return mInjector.features;
+        }
+
+        @Override
         public Context getContext() {
             return mActivity;
         }
@@ -1260,7 +1265,7 @@
                 case TYPE_RECENT_OPEN:
                     if (DEBUG) Log.d(TAG, "Creating new loader recents.");
                     final RootsAccess roots = DocumentsApplication.getRootsCache(context);
-                    return new RecentsLoader(context, roots, mState);
+                    return new RecentsLoader(context, roots, mState, mInjector.features);
 
                 default:
                     throw new IllegalStateException("Unknown type " + mLocalState.mType);
diff --git a/src/com/android/documentsui/dirlist/DocumentsAdapter.java b/src/com/android/documentsui/dirlist/DocumentsAdapter.java
index 8d7c63c..9b1794c 100644
--- a/src/com/android/documentsui/dirlist/DocumentsAdapter.java
+++ b/src/com/android/documentsui/dirlist/DocumentsAdapter.java
@@ -25,6 +25,7 @@
 import android.support.v7.widget.RecyclerView;
 
 import com.android.documentsui.base.EventListener;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.State;
 
 import java.util.List;
@@ -100,6 +101,7 @@
      */
     interface Environment {
         Context getContext();
+        Features getFeatures();
         int getColumnCount();
         State getDisplayState();
         boolean isInSearchMode();
diff --git a/src/com/android/documentsui/dirlist/Message.java b/src/com/android/documentsui/dirlist/Message.java
index 7f9c6aa..d8044bb 100644
--- a/src/com/android/documentsui/dirlist/Message.java
+++ b/src/com/android/documentsui/dirlist/Message.java
@@ -115,7 +115,8 @@
         }
 
         private void updateToRecoverableExceptionHeader(Update event) {
-            assert(Shared.ENABLE_OMC_API_FEATURES);
+            assert(mEnv.getFeatures().isRemoteActionsEnabled());
+
             RootInfo root = mEnv.getDisplayState().stack.getRoot();
             update(mEnv.getContext().getResources().getText(R.string.authentication_required),
                     mEnv.getContext().getString(R.string.open_app, root.title),
diff --git a/src/com/android/documentsui/dirlist/Model.java b/src/com/android/documentsui/dirlist/Model.java
index 3de9c73..43aed9a 100644
--- a/src/com/android/documentsui/dirlist/Model.java
+++ b/src/com/android/documentsui/dirlist/Model.java
@@ -16,10 +16,8 @@
 
 package com.android.documentsui.dirlist;
 
-import static com.android.documentsui.base.DocumentInfo.getCursorInt;
 import static com.android.documentsui.base.DocumentInfo.getCursorString;
 import static com.android.documentsui.base.Shared.DEBUG;
-import static com.android.documentsui.base.Shared.ENABLE_OMC_API_FEATURES;
 import static com.android.documentsui.base.Shared.VERBOSE;
 
 import android.annotation.IntDef;
@@ -35,10 +33,10 @@
 import android.util.Log;
 
 import com.android.documentsui.DirectoryResult;
-import com.android.documentsui.archives.ArchivesProvider;
+import com.android.documentsui.base.DocumentFilters;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.EventListener;
-import com.android.documentsui.base.Shared;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.roots.RootCursorWrapper;
 import com.android.documentsui.selection.Selection;
 
@@ -58,46 +56,26 @@
 @VisibleForTesting
 public class Model {
 
-    /**
-     * Filter that passes (returns true) for all files which can be shared.
-     */
-    public static final Predicate<Cursor> SHARABLE_FILE_FILTER = (Cursor c) -> {
-        int flags = getCursorInt(c, Document.COLUMN_FLAGS);
-        String authority = getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY);
-        if (!ENABLE_OMC_API_FEATURES) {
-            return (flags & Document.FLAG_PARTIAL) == 0
-                    && (flags & Document.FLAG_VIRTUAL_DOCUMENT) == 0
-                    && !ArchivesProvider.AUTHORITY.equals(authority);
-        }
-        return (flags & Document.FLAG_PARTIAL) == 0
-                && !ArchivesProvider.AUTHORITY.equals(authority);
-    };
-
-    /**
-     * Filter that passes (returns true) only virtual documents.
-     */
-    public static final Predicate<Cursor> VIRTUAL_DOCUMENT_FILTER  = (Cursor c) -> {
-        int flags = getCursorInt(c, Document.COLUMN_FLAGS);
-        return (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0;
-    };
-
-    private static final Predicate<Cursor> ANY_FILE_FILTER = (Cursor c) -> true;
-
     private static final String TAG = "Model";
 
+    @Nullable String info;
+    @Nullable String error;
+    @Nullable DocumentInfo doc;
+
+    private final Features mFeatures;
     /** Maps Model ID to cursor positions, for looking up items by Model ID. */
     private final Map<String, Integer> mPositions = new HashMap<>();
     private final Set<String> mFileNames = new HashSet<>();
 
     private boolean mIsLoading;
     private List<EventListener<Update>> mUpdateListeners = new ArrayList<>();
-    @Nullable private Cursor mCursor;
+    private @Nullable Cursor mCursor;
     private int mCursorCount;
     private String mIds[] = new String[0];
 
-    @Nullable String info;
-    @Nullable String error;
-    @Nullable DocumentInfo doc;
+    public Model(Features features) {
+        mFeatures = features;
+    }
 
     public void addUpdateListener(EventListener<Update> listener) {
         mUpdateListeners.add(listener);
@@ -114,7 +92,7 @@
     }
 
     private void notifyUpdateListeners(Exception e) {
-        Update error = new Update(e);
+        Update error = new Update(e, mFeatures.isRemoteActionsEnabled());
         for (EventListener<Update> handler: mUpdateListeners) {
             handler.accept(error);
         }
@@ -227,7 +205,7 @@
     }
 
     public List<DocumentInfo> getDocuments(Selection selection) {
-        return loadDocuments(selection, ANY_FILE_FILTER);
+        return loadDocuments(selection, DocumentFilters.ANY);
     }
 
     public @Nullable DocumentInfo getDocument(String modelId) {
@@ -307,16 +285,19 @@
 
         private final @UpdateType int mUpdateType;
         private final @Nullable Exception mException;
+        private final boolean mRemoteActionEnabled;
 
         private Update() {
             mUpdateType = TYPE_UPDATE;
             mException = null;
+            mRemoteActionEnabled = false;
         }
 
-        public Update(Exception exception) {
+        public Update(Exception exception, boolean remoteActionsEnabled) {
             assert(exception != null);
             mUpdateType = TYPE_UPDATE_EXCEPTION;
             mException = exception;
+            mRemoteActionEnabled = remoteActionsEnabled;
         }
 
         public boolean isUpdate() {
@@ -328,7 +309,8 @@
         }
 
         public boolean hasRecoverableException() {
-            return Shared.ENABLE_OMC_API_FEATURES && hasException()
+            return mRemoteActionEnabled
+                    && hasException()
                     && mException instanceof RecoverableSecurityException;
         }
 
diff --git a/src/com/android/documentsui/files/ActionHandler.java b/src/com/android/documentsui/files/ActionHandler.java
index 602446a..5a7f374 100644
--- a/src/com/android/documentsui/files/ActionHandler.java
+++ b/src/com/android/documentsui/files/ActionHandler.java
@@ -17,7 +17,6 @@
 package com.android.documentsui.files;
 
 import static com.android.documentsui.base.Shared.DEBUG;
-import static com.android.documentsui.base.Shared.ENABLE_OMC_API_FEATURES;
 
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
@@ -27,8 +26,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Root;
-import android.provider.DocumentsContract.Document;
 import android.util.Log;
 
 import com.android.documentsui.AbstractActionHandler;
@@ -41,8 +38,10 @@
 import com.android.documentsui.R;
 import com.android.documentsui.base.ConfirmationCallback;
 import com.android.documentsui.base.ConfirmationCallback.Result;
+import com.android.documentsui.base.DocumentFilters;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.DocumentStack;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.Lookup;
 import com.android.documentsui.base.MimeTypes;
 import com.android.documentsui.base.RootInfo;
@@ -53,14 +52,12 @@
 import com.android.documentsui.clipping.UrisSupplier;
 import com.android.documentsui.dirlist.AnimationView;
 import com.android.documentsui.dirlist.DocumentDetails;
-import com.android.documentsui.dirlist.FocusHandler;
 import com.android.documentsui.dirlist.Model;
 import com.android.documentsui.files.ActionHandler.Addons;
 import com.android.documentsui.queries.SearchViewManager;
 import com.android.documentsui.roots.GetRootDocumentTask;
 import com.android.documentsui.roots.RootsAccess;
 import com.android.documentsui.selection.Selection;
-import com.android.documentsui.selection.SelectionManager;
 import com.android.documentsui.services.FileOperation;
 import com.android.documentsui.services.FileOperationService;
 import com.android.documentsui.services.FileOperations;
@@ -81,8 +78,9 @@
     private static final String TAG = "ManagerActionHandler";
 
     private final ActionModeAddons mActionModeAddons;
+    private final Features mFeatures;
+    private final ActivityConfig mConfig;
     private final DialogController mDialogs;
-    private final ActivityConfig mActConfig;
     private final DocumentClipper mClipper;
     private final ClipStore mClipStore;
     private @Nullable Model mModel;
@@ -102,8 +100,9 @@
         super(activity, state, roots, docs, searchMgr, executors, injector);
 
         mActionModeAddons = actionModeAddons;
+        mFeatures = injector.features;
+        mConfig = injector.config;
         mDialogs = injector.dialogs;
-        mActConfig = injector.config;
         mClipper = clipper;
         mClipStore = clipStore;
     }
@@ -187,7 +186,7 @@
             return false;
         }
 
-        if (mActConfig.isDocumentEnabled(doc.mimeType, doc.flags, mState)) {
+        if (mConfig.isDocumentEnabled(doc.mimeType, doc.flags, mState)) {
             onDocumentPicked(doc);
             mSelectionMgr.clearSelection();
             return true;
@@ -314,8 +313,8 @@
         assert(!selection.isEmpty());
 
         // Model must be accessed in UI thread, since underlying cursor is not threadsafe.
-        List<DocumentInfo> docs =
-                mModel.loadDocuments(selection, Model.SHARABLE_FILE_FILTER);
+        List<DocumentInfo> docs = mModel.loadDocuments(
+                selection, DocumentFilters.sharable(mFeatures));
 
         Intent intent;
 
@@ -346,8 +345,8 @@
         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
 
-        if (Shared.ENABLE_OMC_API_FEATURES
-                && mModel.hasDocuments(selection, Model.VIRTUAL_DOCUMENT_FILTER)) {
+        if (mFeatures.isVirtualFilesSharingEnabled()
+                && mModel.hasDocuments(selection, DocumentFilters.VIRTUAL)) {
             intent.addCategory(Intent.CATEGORY_TYPED_OPENABLE);
         }
 
@@ -420,8 +419,9 @@
 
     private boolean launchToRoot(Intent intent) {
         String action = intent.getAction();
+        // TODO: Remove the "BROWSE" action once our min runtime in O.
         if (Intent.ACTION_VIEW.equals(action)
-                || (!ENABLE_OMC_API_FEATURES && "android.provider.action.BROWSE".equals(action))) {
+                || "android.provider.action.BROWSE".equals(action)) {
             Uri uri = intent.getData();
             if (DocumentsContract.isRootUri(mActivity, uri)) {
                 if (DEBUG) Log.d(TAG, "Launching with root URI.");
@@ -455,7 +455,7 @@
         }
 
         Intent intent = Intent.createChooser(buildViewIntent(doc), null);
-        if (Shared.ENABLE_OMC_API_FEATURES) {
+        if (Features.OMC_RUNTIME) {
             intent.putExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, false);
         }
         try {
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index 5f823ba..b5989b2 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -46,6 +46,7 @@
 import com.android.documentsui.SharedInputHandler;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.DocumentStack;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
 import com.android.documentsui.base.State;
@@ -85,6 +86,7 @@
 
         MessageBuilder messages = new MessageBuilder(this);
         mInjector = new Injector<>(
+                Features.create(getResources()),
                 new Config(),
                 ScopedPreferences.create(this, PREFERENCES_SCOPE),
                 messages,
@@ -96,6 +98,7 @@
         mInjector.selectionMgr = new SelectionManager(SelectionManager.MODE_MULTIPLE);
 
         mInjector.focusManager = new FocusManager(
+                mInjector.features,
                 mInjector.selectionMgr,
                 mDrawer,
                 this::focusSidebar,
@@ -133,7 +136,8 @@
 
         mActivityInputHandler =
                 new ActivityInputHandler(mInjector.actions::deleteSelectedDocuments);
-        mSharedInputHandler = new SharedInputHandler(mInjector.focusManager, this::popDir);
+        mSharedInputHandler =
+                new SharedInputHandler(mInjector.focusManager, this::popDir, mInjector.features);
 
         RootsFragment.show(getFragmentManager(), null);
 
diff --git a/src/com/android/documentsui/picker/ActionHandler.java b/src/com/android/documentsui/picker/ActionHandler.java
index 8a21b48..a92685a 100644
--- a/src/com/android/documentsui/picker/ActionHandler.java
+++ b/src/com/android/documentsui/picker/ActionHandler.java
@@ -17,8 +17,6 @@
 package com.android.documentsui.picker;
 
 import static com.android.documentsui.base.Shared.DEBUG;
-import static com.android.documentsui.base.State.ACTION_GET_CONTENT;
-import static com.android.documentsui.base.State.ACTION_PICK_COPY_DESTINATION;
 
 import android.app.Activity;
 import android.content.Intent;
@@ -35,21 +33,16 @@
 import com.android.documentsui.Metrics;
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.DocumentStack;
-import com.android.documentsui.base.EventListener;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.Lookup;
-import com.android.documentsui.base.MimeTypes;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
 import com.android.documentsui.base.State;
-import com.android.documentsui.dirlist.AnimationView;
 import com.android.documentsui.dirlist.DocumentDetails;
-import com.android.documentsui.dirlist.FocusHandler;
 import com.android.documentsui.dirlist.Model;
-import com.android.documentsui.dirlist.Model.Update;
 import com.android.documentsui.picker.ActionHandler.Addons;
 import com.android.documentsui.queries.SearchViewManager;
 import com.android.documentsui.roots.RootsAccess;
-import com.android.documentsui.selection.SelectionManager;
 
 import java.util.concurrent.Executor;
 
@@ -62,6 +55,7 @@
 
     private static final String TAG = "PickerActionHandler";
 
+    private final Features mFeatures;
     private final ActivityConfig mConfig;
     private @Nullable Model mModel;
 
@@ -77,6 +71,7 @@
         super(activity, state, roots, docs, searchMgr, executors, injector);
 
         mConfig = injector.config;
+        mFeatures = injector.features;
     }
 
     @Override
@@ -99,7 +94,7 @@
             return;
         }
 
-        if (Shared.ENABLE_OMC_API_FEATURES && launchToDocument(intent)) {
+        if (mFeatures.isLaunchToDocumentEnabled() && launchToDocument(intent)) {
             if (DEBUG) Log.d(TAG, "Launched to a document.");
             return;
         }
diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java
index 315f432..40c307a 100644
--- a/src/com/android/documentsui/picker/PickActivity.java
+++ b/src/com/android/documentsui/picker/PickActivity.java
@@ -53,6 +53,7 @@
 import com.android.documentsui.R;
 import com.android.documentsui.SharedInputHandler;
 import com.android.documentsui.base.DocumentInfo;
+import com.android.documentsui.base.Features;
 import com.android.documentsui.base.MimeTypes;
 import com.android.documentsui.base.PairedTask;
 import com.android.documentsui.base.RootInfo;
@@ -89,6 +90,7 @@
     public void onCreate(Bundle icicle) {
 
         mInjector = new Injector<>(
+                Features.create(getResources()),
                 new Config(),
                 ScopedPreferences.create(this, PREFERENCES_SCOPE),
                 new MessageBuilder(this),
@@ -102,6 +104,7 @@
                         : SelectionManager.MODE_SINGLE);
 
         mInjector.focusManager = new FocusManager(
+                mInjector.features,
                 mInjector.selectionMgr,
                 mDrawer,
                 this::focusSidebar,
@@ -124,8 +127,8 @@
 
         Intent intent = getIntent();
 
-        mSharedInputHandler = new SharedInputHandler(mInjector.focusManager, this::popDir);
-
+        mSharedInputHandler =
+                new SharedInputHandler(mInjector.focusManager, this::popDir, mInjector.features);
         setupLayout(intent);
         mInjector.actions.initLocation(intent);
     }
diff --git a/src/com/android/documentsui/prefs/ScopedPreferences.java b/src/com/android/documentsui/prefs/ScopedPreferences.java
index fd0f0fe..930927d 100644
--- a/src/com/android/documentsui/prefs/ScopedPreferences.java
+++ b/src/com/android/documentsui/prefs/ScopedPreferences.java
@@ -76,7 +76,7 @@
 
         @Override
         public boolean getEnableArchiveCreation() {
-            final boolean defaultValue = mResources.getBoolean(R.bool.enable_archive_creation);
+            final boolean defaultValue = mResources.getBoolean(R.bool.feature_archive_creation);
             return mSharedPrefs.getBoolean(ENABLE_ARCHIVE_CREATION + mScope, defaultValue);
         }
 
diff --git a/src/com/android/documentsui/queries/DebugCommandProcessor.java b/src/com/android/documentsui/queries/DebugCommandProcessor.java
index ff62526..7b36d5b 100644
--- a/src/com/android/documentsui/queries/DebugCommandProcessor.java
+++ b/src/com/android/documentsui/queries/DebugCommandProcessor.java
@@ -24,7 +24,6 @@
 import com.android.documentsui.DocumentsApplication;
 import com.android.documentsui.base.DebugFlags;
 import com.android.documentsui.base.EventHandler;
-import com.android.documentsui.base.Shared;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -111,11 +110,6 @@
     }
 
     private static boolean forcePaging(String[] tokens) {
-        if (!Shared.ENABLE_OMC_API_FEATURES) {
-            Log.i(TAG, "Paging is disabled.");
-            return false;
-        }
-
         if ("page".equals(tokens[0])) {
             if (tokens.length >= 2) {
                 try {
diff --git a/src/com/android/documentsui/sidebar/RootsList.java b/src/com/android/documentsui/sidebar/RootsList.java
index 1de2710..b83fcbf 100644
--- a/src/com/android/documentsui/sidebar/RootsList.java
+++ b/src/com/android/documentsui/sidebar/RootsList.java
@@ -20,7 +20,7 @@
 import android.view.KeyEvent;
 import android.widget.ListView;
 
-import com.android.documentsui.base.Shared;
+import com.android.documentsui.base.Features;
 
 /**
  * The list in the navigation drawer. This class exists for the purpose of overriding the key
@@ -29,22 +29,28 @@
  */
 public class RootsList extends ListView {
 
+    private final Features mFeatures;
+
     // Multiple constructors are needed to handle all the different ways this View could be
     // constructed by the framework. Don't remove them!
     public RootsList(Context context) {
         super(context);
+        mFeatures = Features.create(getResources());
     }
 
     public RootsList(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        mFeatures = Features.create(getResources());
     }
 
     public RootsList(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        mFeatures = Features.create(getResources());
     }
 
     public RootsList(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mFeatures = Features.create(getResources());
     }
 
     @Override
@@ -53,7 +59,8 @@
             // Ignore tab key events - this causes them to bubble up to the global key handler where
             // they are appropriately handled. See BaseActivity.onKeyDown.
             case KeyEvent.KEYCODE_TAB:
-                return Shared.ENABLE_OMC_API_FEATURES ? super.onKeyDown(keyCode, event) : false;
+                return mFeatures.isSystemKeyboardNavigationEnabled()
+                        && super.onKeyDown(keyCode, event);
             // Prevent left/right arrow keystrokes from shifting focus away from the roots list.
             case KeyEvent.KEYCODE_DPAD_LEFT:
             case KeyEvent.KEYCODE_DPAD_RIGHT:
diff --git a/src/com/android/documentsui/sorting/SortModel.java b/src/com/android/documentsui/sorting/SortModel.java
index e3eb1e9..a61543f 100644
--- a/src/com/android/documentsui/sorting/SortModel.java
+++ b/src/com/android/documentsui/sorting/SortModel.java
@@ -17,8 +17,6 @@
 package com.android.documentsui.sorting;
 
 import static com.android.documentsui.base.Shared.DEBUG;
-import static com.android.documentsui.base.Shared.ENABLE_OMC_API_FEATURES;
-import static com.android.documentsui.base.Shared.VERBOSE;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
@@ -33,7 +31,6 @@
 import android.view.View;
 
 import com.android.documentsui.R;
-import com.android.documentsui.base.Shared;
 import com.android.documentsui.sorting.SortDimension.SortDirection;
 
 import java.lang.annotation.Retention;
@@ -225,15 +222,6 @@
     }
 
     public Cursor sortCursor(Cursor cursor) {
-        if (ENABLE_OMC_API_FEATURES
-                && cursor.getExtras().containsKey(ContentResolver.QUERY_ARG_SORT_COLUMNS)) {
-            if (VERBOSE) Log.i(TAG, "Cursor is pre-sorted by provider. Skipping sort. Booya!");
-
-            // TODO: assert that the contents of QUERY_ARG_SORT_COLUMNS
-            // matches the sort dimension...once we're returning any pre-sorted results.
-            return cursor;
-        }
-
         if (mSortedDimension != null) {
             return new SortingCursorWrapper(cursor, mSortedDimension);
         } else {
@@ -242,7 +230,7 @@
     }
 
     public void addQuerySortArgs(Bundle queryArgs) {
-        assert(Shared.ENABLE_OMC_API_FEATURES);
+        // should only be called when R.bool.feature_content_paging is true
 
         final int id = getSortedDimensionId();
         switch (id) {
@@ -287,7 +275,12 @@
     }
 
     public @Nullable String getDocumentSortQuery() {
-        assert(!Shared.ENABLE_OMC_API_FEATURES);
+        // This method should only be called when R.bool.feature_content_paging is false.
+        // Once that feature is enabled by default, this method should be removed.
+        // The following assert exists simply to make reference to the resource id
+        // so the compiler will fail when the feature is removed...reminding you and me
+        // to remove this method :)
+        assert(R.bool.feature_content_paging != Integer.MIN_VALUE);
 
         final int id = getSortedDimensionId();
         final String columnName;