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);
}
}