Ignore disabled activities in Application Provider.

Call the package manager to check whether an app is disabled.

Bug: 5029707
Change-Id: Ic338e990ae78dfde1ee6112d5fd69129c74fc50c
diff --git a/src/com/android/providers/applications/ApplicationsProvider.java b/src/com/android/providers/applications/ApplicationsProvider.java
index 1177aa8..04ed5db 100644
--- a/src/com/android/providers/applications/ApplicationsProvider.java
+++ b/src/com/android/providers/applications/ApplicationsProvider.java
@@ -117,8 +117,6 @@
     // Handler that runs DB updates.
     private Handler mHandler;
 
-    private Runnable onApplicationsListUpdated;
-
     /**
      * We delay application updates by this many millis to avoid doing more than one update to the
      * applications list within this window.
@@ -177,13 +175,19 @@
         // Start thread that runs app updates
         HandlerThread thread = new HandlerThread("ApplicationsProviderUpdater", THREAD_PRIORITY);
         thread.start();
-        mHandler = new UpdateHandler(thread.getLooper());
+        mHandler = createHandler(thread.getLooper());
         // Kick off first apps update
         postUpdateAll();
         return true;
     }
 
-    private class UpdateHandler extends Handler {
+    @VisibleForTesting
+    Handler createHandler(Looper looper) {
+        return new UpdateHandler(looper);
+    }
+
+    @VisibleForTesting
+    class UpdateHandler extends Handler {
 
         public UpdateHandler(Looper looper) {
             super(looper);
@@ -503,26 +507,29 @@
                 }
 
                 String activityPackageName = info.activityInfo.applicationInfo.packageName;
-                PkgUsageStats stats = usageStats.get(activityPackageName);
-                int launchCount = 0;
-                long lastResumeTime = 0;
-                if (stats != null) {
-                    launchCount = stats.launchCount;
-                    if (stats.componentResumeTimes.containsKey(activityClassName)) {
-                        lastResumeTime = stats.componentResumeTimes.get(activityClassName);
+                if (DBG) Log.d(TAG, "activity " + activityPackageName + "/" + activityClassName);
+                if (isComponentEnabled(manager, activityPackageName, activityClassName)) {
+                    PkgUsageStats stats = usageStats.get(activityPackageName);
+                    int launchCount = 0;
+                    long lastResumeTime = 0;
+                    if (stats != null) {
+                        launchCount = stats.launchCount;
+                        if (stats.componentResumeTimes.containsKey(activityClassName)) {
+                            lastResumeTime = stats.componentResumeTimes.get(activityClassName);
+                        }
                     }
-                }
 
-                String icon = getActivityIconUri(info.activityInfo);
-                inserter.prepareForInsert();
-                inserter.bind(nameCol, title);
-                inserter.bind(descriptionCol, description);
-                inserter.bind(packageCol, activityPackageName);
-                inserter.bind(classCol, activityClassName);
-                inserter.bind(iconCol, icon);
-                inserter.bind(launchCountCol, launchCount);
-                inserter.bind(lastResumeTimeCol, lastResumeTime);
-                inserter.execute();
+                    String icon = getActivityIconUri(info.activityInfo);
+                    inserter.prepareForInsert();
+                    inserter.bind(nameCol, title);
+                    inserter.bind(descriptionCol, description);
+                    inserter.bind(packageCol, activityPackageName);
+                    inserter.bind(classCol, activityClassName);
+                    inserter.bind(iconCol, icon);
+                    inserter.bind(launchCountCol, launchCount);
+                    inserter.bind(lastResumeTimeCol, lastResumeTime);
+                    inserter.execute();
+                }
             }
             mDb.setTransactionSuccessful();
         } finally {
@@ -530,11 +537,23 @@
             inserter.close();
         }
 
-        if (onApplicationsListUpdated != null) {
-            onApplicationsListUpdated.run();
+        if (DBG) Log.d(TAG, "Finished updating database.");
+    }
+
+    private boolean isComponentEnabled(PackageManager manager, String packageName,
+            String componentName) {
+        if (manager.getApplicationEnabledSetting(packageName) ==
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+            if (DBG) Log.d(TAG, "DISABLED package " + packageName);
+            return false;
+        }
+        if (manager.getComponentEnabledSetting(new ComponentName(packageName, componentName)) ==
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+            if (DBG) Log.d(TAG, "DISABLED component " + packageName + "/" + componentName);
+            return false;
         }
 
-        if (DBG) Log.d(TAG, "Finished updating database.");
+        return true;
     }
 
     @VisibleForTesting
@@ -672,9 +691,4 @@
                 getContext().checkCallingPermission(android.Manifest.permission.GLOBAL_SEARCH));
     }
 
-    @VisibleForTesting
-    protected void setOnApplicationsListUpdated(Runnable onApplicationsListUpdated) {
-        this.onApplicationsListUpdated = onApplicationsListUpdated;
-    }
-
 }
diff --git a/tests/src/com/android/providers/applications/ApplicationsProviderForTesting.java b/tests/src/com/android/providers/applications/ApplicationsProviderForTesting.java
index 90b6711..56a3d80 100644
--- a/tests/src/com/android/providers/applications/ApplicationsProviderForTesting.java
+++ b/tests/src/com/android/providers/applications/ApplicationsProviderForTesting.java
@@ -17,9 +17,15 @@
 package com.android.providers.applications;
 
 import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
 import com.android.internal.os.PkgUsageStats;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -33,6 +39,8 @@
 
     private boolean mHasGlobalSearchPermission;
 
+    private MockUpdateHandler mMockUpdateHandler;
+
     @Override
     protected PackageManager getPackageManager() {
         return mMockPackageManager;
@@ -63,4 +71,38 @@
     protected boolean hasGlobalSearchPermission() {
         return mHasGlobalSearchPermission;
     }
+
+    @Override
+    Handler createHandler(Looper looper) {
+        mMockUpdateHandler = new MockUpdateHandler(looper);
+        return mMockUpdateHandler;
+    }
+
+    public boolean dispatchNextMessage() {
+        return mMockUpdateHandler.dispatchNextMessage();
+    }
+
+    private class MockUpdateHandler extends UpdateHandler {
+
+        List<Message> mMessages = new ArrayList<Message>();
+
+        MockUpdateHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            mMessages.add(msg);
+            return true;
+        }
+
+        public boolean dispatchNextMessage() {
+            if (!mMessages.isEmpty()) {
+                dispatchMessage(mMessages.remove(0));
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
 }
diff --git a/tests/src/com/android/providers/applications/ApplicationsProviderTest.java b/tests/src/com/android/providers/applications/ApplicationsProviderTest.java
index d512590..99a8a2e 100644
--- a/tests/src/com/android/providers/applications/ApplicationsProviderTest.java
+++ b/tests/src/com/android/providers/applications/ApplicationsProviderTest.java
@@ -19,11 +19,12 @@
 import android.app.SearchManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.Applications;
 import android.test.ProviderTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import java.util.concurrent.FutureTask;
 
@@ -36,7 +37,7 @@
  * is also created in an isolated context so it doesn't interfere with the
  * database of the actual ApplicationsProvider installed on the device.
  */
-@LargeTest
+@SmallTest
 public class ApplicationsProviderTest extends ProviderTestCase2<ApplicationsProviderForTesting> {
 
     private ApplicationsProviderForTesting mProvider;
@@ -52,7 +53,6 @@
         super.setUp();
         mProvider = getProvider();
         mMockActivityManager = new MockActivityManager();
-        initProvider(mProvider);
     }
 
     /**
@@ -62,21 +62,16 @@
         // Decouple the provider from Android's real list of applications.
         MockPackageManager mockPackageManager = new MockPackageManager();
         addDefaultTestPackages(mockPackageManager);
+
+        initProvider(provider, mockPackageManager);
+    }
+
+    private void initProvider(ApplicationsProviderForTesting provider,
+            MockPackageManager mockPackageManager) throws Exception {
         provider.setMockPackageManager(mockPackageManager);
         provider.setMockActivityManager(mMockActivityManager);
 
-        // We need to wait for the applications database to be updated (it's
-        // updated with a slight delay by a separate thread) before we can use
-        // the ApplicationsProvider.
-        Runnable markerRunnable = new Runnable() {
-            @Override
-            public void run() {
-            }
-        };
-        FutureTask<Void> onApplicationsListUpdated = new FutureTask<Void>(markerRunnable, null);
-
-        provider.setOnApplicationsListUpdated(onApplicationsListUpdated);
-        onApplicationsListUpdated.get();
+        assertTrue(provider.dispatchNextMessage());
     }
 
     /**
@@ -99,24 +94,29 @@
         mockPackageManager.addPackage("AlphabeticD2", new ComponentName("d", "d.DView2"));
     }
 
-    public void testSearch_singleResult() {
+    public void testSearch_singleResult() throws Exception {
+        initProvider(mProvider);
         testSearch("ema", "Email");
     }
 
-    public void testSearch_multipleResults() {
+    public void testSearch_multipleResults() throws Exception {
+        initProvider(mProvider);
         testSearch("e", "Ebay", "Email");
     }
 
-    public void testSearch_noResults() {
+    public void testSearch_noResults() throws Exception {
+        initProvider(mProvider);
         testSearch("nosuchapp");
     }
 
-    public void testSearch_orderingIsAlphabeticByDefault() {
+    public void testSearch_orderingIsAlphabeticByDefault() throws Exception {
+        initProvider(mProvider);
         testSearch("alphabetic", "AlphabeticA", "AlphabeticB", "AlphabeticC", "AlphabeticD",
                 "AlphabeticD2");
     }
 
-    public void testSearch_emptySearchQueryReturnsEverything() {
+    public void testSearch_emptySearchQueryReturnsEverything() throws Exception {
+        initProvider(mProvider);
         testSearch("",
                 "AlphabeticA", "AlphabeticB", "AlphabeticC", "AlphabeticD", "AlphabeticD2",
                 "Ebay", "Email", "Fakeapp");
@@ -129,13 +129,9 @@
         mMockActivityManager.addLastResumeTime("c", "c.CView", 0);
 
         // Launch count database is populated on startup.
-        mProvider = createNewProvider(getMockContext());
         mProvider.setHasGlobalSearchPermission(true);
-        initProvider(mProvider);
 
-        // Override the previous provider with the new instance in the
-        // ContentResolver.
-        getMockContentResolver().addProvider(Applications.AUTHORITY, mProvider);
+        initProvider(mProvider);
 
         // New ranking: D, B, A, C (first by launch count, then
         // - if the launch counts of two apps are equal - alphabetically)
@@ -143,7 +139,8 @@
                 "AlphabeticD2");
     }
 
-    public void testSearch_appsAreRankedByResumeTimeAfterUpdate() {
+    public void testSearch_appsAreRankedByResumeTimeAfterUpdate() throws Exception {
+        initProvider(mProvider);
         mProvider.setHasGlobalSearchPermission(true);
 
         mMockActivityManager.addLastResumeTime("d", "d.DView", 3);
@@ -161,7 +158,8 @@
                 "AlphabeticD2");
     }
 
-    public void testSearch_noLastAccessTimesWithoutPermission() {
+    public void testSearch_noLastAccessTimesWithoutPermission() throws Exception {
+        initProvider(mProvider);
         mProvider.setHasGlobalSearchPermission(false);
         mMockActivityManager.addLastResumeTime("d", "d.DView", 1);
         mMockActivityManager.addLastResumeTime("b", "b.BView", 2);
@@ -173,7 +171,8 @@
         assertEquals(-1, cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT));
     }
 
-    public void testSearch_lastAccessTimes() {
+    public void testSearch_lastAccessTimes() throws Exception {
+        initProvider(mProvider);
         mProvider.setHasGlobalSearchPermission(true);
         mMockActivityManager.addLastResumeTime("d", "d.DView", 1);
         mMockActivityManager.addLastResumeTime("b", "b.BView", 2);
@@ -196,7 +195,8 @@
      * is a privileged application - ordering apps by launch count when asked
      * by a regular application would leak information about user behavior.
      */
-    public void testSearch_notAllowedToRankByLaunchCount() {
+    public void testSearch_notAllowedToRankByLaunchCount() throws Exception {
+        initProvider(mProvider);
         // Simulate non-privileged calling application.
         mProvider.setHasGlobalSearchPermission(false);
 
@@ -214,6 +214,34 @@
                 "AlphabeticD2");
     }
 
+    public void testSearch_disabledPackage() throws Exception {
+        MockPackageManager mockPackageManager = new MockPackageManager();
+        mockPackageManager.addPackage("DisabledPackageApp1",
+                new ComponentName("dp", "dp.DisView1"));
+        mockPackageManager.addPackage("DisabledPackageApp2",
+                new ComponentName("dp", "dp.DisView2"),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+        initProvider(mProvider, mockPackageManager);
+
+        mProvider.setHasGlobalSearchPermission(true);
+        testSearch("dis");
+    }
+
+    public void testSearch_disabledComponent() throws Exception {
+        MockPackageManager mockPackageManager = new MockPackageManager();
+        mockPackageManager.addPackage("DisabledApp1", new ComponentName("da", "da.DaView1"),
+                PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+        mockPackageManager.addPackage("DisabledApp2", new ComponentName("da", "da.DaView2"),
+                PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+                PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+        initProvider(mProvider, mockPackageManager);
+
+        mProvider.setHasGlobalSearchPermission(true);
+        testSearch("dis", "DisabledApp2");
+    }
+
     private void testSearch(String searchQuery, String... expectedResultsInOrder) {
         Cursor cursor = Applications.search(getMockContentResolver(), searchQuery);
 
@@ -274,11 +302,4 @@
             cursor.moveToNext();
         }
     }
-
-    private ApplicationsProviderForTesting createNewProvider(Context context) throws Exception {
-        ApplicationsProviderForTesting newProviderInstance =
-                ApplicationsProviderForTesting.class.newInstance();
-        newProviderInstance.attachInfo(context, null);
-        return newProviderInstance;
-    }
 }
diff --git a/tests/src/com/android/providers/applications/MockPackageManager.java b/tests/src/com/android/providers/applications/MockPackageManager.java
index 3d272c4..9db7ee5 100644
--- a/tests/src/com/android/providers/applications/MockPackageManager.java
+++ b/tests/src/com/android/providers/applications/MockPackageManager.java
@@ -26,12 +26,17 @@
 import android.content.pm.ResolveInfo;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class MockPackageManager extends android.test.mock.MockPackageManager {
 
     private List<ResolveInfo> mPackages = new ArrayList<ResolveInfo>();
 
+    private Map<String, Integer> mApplicationEnabledSettings = new HashMap<String, Integer>();
+    private Map<String, Integer> mComponentEnabledSettings = new HashMap<String, Integer>();
+
     /**
      * Returns all packages registered with the mock package manager.
      * ApplicationsProvider uses this method to query the list of applications.
@@ -41,16 +46,34 @@
         return mPackages;
     }
 
+    @Override
+    public int getApplicationEnabledSetting(String packageName) {
+        return mApplicationEnabledSettings.get(packageName);
+    }
+
+    @Override
+    public int getComponentEnabledSetting(ComponentName componentName) {
+        return mComponentEnabledSettings.get(componentName.flattenToString());
+    }
+
     /**
      * Adds a new package to the mock package manager.
      *
      * Example:
-     * addPackage("Email", new ComponentName("com.android.email", "com.android.email.MainView"));
+     * addPackage("Email", new ComponentName("com.android.email", "com.android.email.MainView"),
+     *            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+     *            PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
      *
      * @param title the user-friendly title of the application (this is what
      *         users will search for)
+     * @param componentName The full component name of the app.
+     * @param appEnabledSetting The setting which should be returned from
+     *        {@link #getApplicationEnabledSetting}, for this component's package.
+     * @param componentEnabledSetting The setting which should be returned from
+     *        {@link #getComponentEnabledSetting}, for this component.
      */
-    public void addPackage(final String title, ComponentName componentName) {
+    public void addPackage(final String title, ComponentName componentName, int appEnabledSetting,
+            int componentEnabledSetting) {
         // Set the application's title.
         ResolveInfo packageInfo = new ResolveInfo() {
             @Override
@@ -66,5 +89,13 @@
         packageInfo.activityInfo.applicationInfo.packageName = componentName.getPackageName();
 
         mPackages.add(packageInfo);
+
+        mApplicationEnabledSettings.put(componentName.getPackageName(), appEnabledSetting);
+        mComponentEnabledSettings.put(componentName.flattenToString(), componentEnabledSetting);
+    }
+
+    public void addPackage(final String title, ComponentName componentName) {
+        addPackage(title, componentName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+                PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
     }
 }