Loading services/core/java/com/android/server/search/Searchables.java +3 −0 Original line number Diff line number Diff line Loading @@ -147,6 +147,9 @@ public class Searchables { Log.e(LOG_TAG, "Error getting activity info " + re); return null; } if (ai == null) { return null; } String refActivityName = null; // First look for activity-specific reference Loading services/tests/servicestests/src/com/android/server/search/SearchablesTest.java +53 −262 Original line number Diff line number Diff line Loading @@ -16,40 +16,59 @@ package com.android.server.search; import android.app.SearchManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import android.app.SearchableInfo; import android.app.SearchableInfo.ActionKeyInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.RemoteException; import com.android.server.search.Searchables; import android.test.AndroidTestCase; import android.content.pm.PackageManagerInternal; import android.test.MoreAsserts; import android.test.mock.MockContext; import android.test.mock.MockPackageManager; import android.test.suitebuilder.annotation.SmallTest; import android.view.KeyEvent; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; /** * To launch this test from the command line: * * adb shell am instrument -w \ * -e class com.android.unit_tests.SearchablesTest \ * com.android.unit_tests/android.test.InstrumentationTestRunner */ @RunWith(AndroidJUnit4.class) @SmallTest public class SearchablesTest extends AndroidTestCase { public class SearchablesTest { @Mock protected PackageManagerInternal mPackageManagerInternal; private Context mContext; @Before public final void setUp() { MockitoAnnotations.initMocks(this); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); } @After public final void tearDown() { Mockito.framework().clearInlineMocks(); } /* * SearchableInfo tests Loading @@ -69,15 +88,15 @@ public class SearchablesTest extends AndroidTestCase { * Test that non-searchable activities return no searchable info (this would typically * trigger the use of the default searchable e.g. contacts) */ @Test public void testNonSearchable() { // test basic array & hashmap Searchables searchables = new Searchables(mContext, 0); searchables.updateSearchableList(); // confirm that we return null for non-searchy activities ComponentName nonActivity = new ComponentName( "com.android.frameworks.coretests", "com.android.frameworks.coretests.activity.NO_SEARCH_ACTIVITY"); ComponentName nonActivity = new ComponentName("com.android.frameworks.servicestests", "com.android.frameworks.servicestests.activity.NO_SEARCH_ACTIVITY"); SearchableInfo si = searchables.getSearchableInfo(nonActivity); assertNull(si); } Loading @@ -97,13 +116,11 @@ public class SearchablesTest extends AndroidTestCase { * getIcon works */ @Test public void testSearchablesListReal() { MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager()); MyMockContext mockContext = new MyMockContext(mContext, mockPM); doReturn(true).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt()); // build item list with real-world source data mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH); Searchables searchables = new Searchables(mockContext, 0); Searchables searchables = new Searchables(mContext, 0); searchables.updateSearchableList(); // tests with "real" searchables (deprecate, this should be a unit test) ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList(); Loading @@ -117,12 +134,11 @@ public class SearchablesTest extends AndroidTestCase { /** * This round of tests confirms good operations with "zero" searchables found */ @Test public void testSearchablesListEmpty() { MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager()); MyMockContext mockContext = new MyMockContext(mContext, mockPM); doReturn(false).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt()); mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO); Searchables searchables = new Searchables(mockContext, 0); Searchables searchables = new Searchables(mContext, 0); searchables.updateSearchableList(); ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList(); assertNotNull(searchablesList); Loading Loading @@ -220,230 +236,5 @@ public class SearchablesTest extends AndroidTestCase { MoreAsserts.assertNotEqual(s, ""); } } /** * This is a mock for context. Used to perform a true unit test on SearchableInfo. * */ private class MyMockContext extends MockContext { protected Context mRealContext; protected PackageManager mPackageManager; /** * Constructor. * * @param realContext Please pass in a real context for some pass-throughs to function. */ MyMockContext(Context realContext, PackageManager packageManager) { mRealContext = realContext; mPackageManager = packageManager; } /** * Resources. Pass through for now. */ @Override public Resources getResources() { return mRealContext.getResources(); } /** * Package manager. Pass through for now. */ @Override public PackageManager getPackageManager() { return mPackageManager; } /** * Package manager. Pass through for now. */ @Override public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException { return mRealContext.createPackageContext(packageName, flags); } /** * Message broadcast. Pass through for now. */ @Override public void sendBroadcast(Intent intent) { mRealContext.sendBroadcast(intent); } } /** * This is a mock for package manager. Used to perform a true unit test on SearchableInfo. * */ private class MyMockPackageManager extends MockPackageManager { public final static int SEARCHABLES_PASSTHROUGH = 0; public final static int SEARCHABLES_MOCK_ZERO = 1; public final static int SEARCHABLES_MOCK_ONEGOOD = 2; public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3; protected PackageManager mRealPackageManager; protected int mSearchablesMode; public MyMockPackageManager(PackageManager realPM) { mRealPackageManager = realPM; mSearchablesMode = SEARCHABLES_PASSTHROUGH; } /** * Set the mode for various tests. */ public void setSearchablesMode(int newMode) { switch (newMode) { case SEARCHABLES_PASSTHROUGH: case SEARCHABLES_MOCK_ZERO: mSearchablesMode = newMode; break; default: throw new UnsupportedOperationException(); } } /** * Find activities that support a given intent. * * Retrieve all activities that can be performed for the given intent. * * @param intent The desired intent as per resolveActivity(). * @param flags Additional option flags. The most important is * MATCH_DEFAULT_ONLY, to limit the resolution to only * those activities that support the CATEGORY_DEFAULT. * * @return A List<ResolveInfo> containing one entry for each matching * Activity. These are ordered from best to worst match -- that * is, the first item in the list is what is returned by * resolveActivity(). If there are no matching activities, an empty * list is returned. */ @Override public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { assertNotNull(intent); assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH) || intent.getAction().equals(Intent.ACTION_WEB_SEARCH) || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.queryIntentActivities(intent, flags); case SEARCHABLES_MOCK_ZERO: return null; default: throw new UnsupportedOperationException(); } } @Override public ResolveInfo resolveActivity(Intent intent, int flags) { assertNotNull(intent); assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH) || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.resolveActivity(intent, flags); case SEARCHABLES_MOCK_ZERO: return null; default: throw new UnsupportedOperationException(); } } /** * Retrieve an XML file from a package. This is a low-level API used to * retrieve XML meta data. * * @param packageName The name of the package that this xml is coming from. * Can not be null. * @param resid The resource identifier of the desired xml. Can not be 0. * @param appInfo Overall information about <var>packageName</var>. This * may be null, in which case the application information will be retrieved * for you if needed; if you already have this information around, it can * be much more efficient to supply it here. * * @return Returns an TypedXmlPullParser allowing you to parse out the XML * data. Returns null if the xml resource could not be found for any * reason. */ @Override public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) { assertNotNull(packageName); MoreAsserts.assertNotEqual(packageName, ""); MoreAsserts.assertNotEqual(resid, 0); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.getXml(packageName, resid, appInfo); case SEARCHABLES_MOCK_ZERO: default: throw new UnsupportedOperationException(); } } /** * Find a single content provider by its base path name. * * @param name The name of the provider to find. * @param flags Additional option flags. Currently should always be 0. * * @return ContentProviderInfo Information about the provider, if found, * else null. */ @Override public ProviderInfo resolveContentProvider(String name, int flags) { assertNotNull(name); MoreAsserts.assertNotEqual(name, ""); assertEquals(flags, 0); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.resolveContentProvider(name, flags); case SEARCHABLES_MOCK_ZERO: default: throw new UnsupportedOperationException(); } } /** * Get the activity information for a particular activity. * * @param name The name of the activity to find. * @param flags Additional option flags. * * @return ActivityInfo Information about the activity, if found, else null. */ @Override public ActivityInfo getActivityInfo(ComponentName name, int flags) throws NameNotFoundException { assertNotNull(name); MoreAsserts.assertNotEqual(name, ""); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.getActivityInfo(name, flags); case SEARCHABLES_MOCK_ZERO: throw new NameNotFoundException(); default: throw new UnsupportedOperationException(); } } @Override public int checkPermission(String permName, String pkgName) { assertNotNull(permName); assertNotNull(pkgName); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.checkPermission(permName, pkgName); case SEARCHABLES_MOCK_ZERO: return PackageManager.PERMISSION_DENIED; default: throw new UnsupportedOperationException(); } } } } Loading
services/core/java/com/android/server/search/Searchables.java +3 −0 Original line number Diff line number Diff line Loading @@ -147,6 +147,9 @@ public class Searchables { Log.e(LOG_TAG, "Error getting activity info " + re); return null; } if (ai == null) { return null; } String refActivityName = null; // First look for activity-specific reference Loading
services/tests/servicestests/src/com/android/server/search/SearchablesTest.java +53 −262 Original line number Diff line number Diff line Loading @@ -16,40 +16,59 @@ package com.android.server.search; import android.app.SearchManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import android.app.SearchableInfo; import android.app.SearchableInfo.ActionKeyInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.RemoteException; import com.android.server.search.Searchables; import android.test.AndroidTestCase; import android.content.pm.PackageManagerInternal; import android.test.MoreAsserts; import android.test.mock.MockContext; import android.test.mock.MockPackageManager; import android.test.suitebuilder.annotation.SmallTest; import android.view.KeyEvent; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; /** * To launch this test from the command line: * * adb shell am instrument -w \ * -e class com.android.unit_tests.SearchablesTest \ * com.android.unit_tests/android.test.InstrumentationTestRunner */ @RunWith(AndroidJUnit4.class) @SmallTest public class SearchablesTest extends AndroidTestCase { public class SearchablesTest { @Mock protected PackageManagerInternal mPackageManagerInternal; private Context mContext; @Before public final void setUp() { MockitoAnnotations.initMocks(this); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); } @After public final void tearDown() { Mockito.framework().clearInlineMocks(); } /* * SearchableInfo tests Loading @@ -69,15 +88,15 @@ public class SearchablesTest extends AndroidTestCase { * Test that non-searchable activities return no searchable info (this would typically * trigger the use of the default searchable e.g. contacts) */ @Test public void testNonSearchable() { // test basic array & hashmap Searchables searchables = new Searchables(mContext, 0); searchables.updateSearchableList(); // confirm that we return null for non-searchy activities ComponentName nonActivity = new ComponentName( "com.android.frameworks.coretests", "com.android.frameworks.coretests.activity.NO_SEARCH_ACTIVITY"); ComponentName nonActivity = new ComponentName("com.android.frameworks.servicestests", "com.android.frameworks.servicestests.activity.NO_SEARCH_ACTIVITY"); SearchableInfo si = searchables.getSearchableInfo(nonActivity); assertNull(si); } Loading @@ -97,13 +116,11 @@ public class SearchablesTest extends AndroidTestCase { * getIcon works */ @Test public void testSearchablesListReal() { MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager()); MyMockContext mockContext = new MyMockContext(mContext, mockPM); doReturn(true).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt()); // build item list with real-world source data mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH); Searchables searchables = new Searchables(mockContext, 0); Searchables searchables = new Searchables(mContext, 0); searchables.updateSearchableList(); // tests with "real" searchables (deprecate, this should be a unit test) ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList(); Loading @@ -117,12 +134,11 @@ public class SearchablesTest extends AndroidTestCase { /** * This round of tests confirms good operations with "zero" searchables found */ @Test public void testSearchablesListEmpty() { MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager()); MyMockContext mockContext = new MyMockContext(mContext, mockPM); doReturn(false).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt()); mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO); Searchables searchables = new Searchables(mockContext, 0); Searchables searchables = new Searchables(mContext, 0); searchables.updateSearchableList(); ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList(); assertNotNull(searchablesList); Loading Loading @@ -220,230 +236,5 @@ public class SearchablesTest extends AndroidTestCase { MoreAsserts.assertNotEqual(s, ""); } } /** * This is a mock for context. Used to perform a true unit test on SearchableInfo. * */ private class MyMockContext extends MockContext { protected Context mRealContext; protected PackageManager mPackageManager; /** * Constructor. * * @param realContext Please pass in a real context for some pass-throughs to function. */ MyMockContext(Context realContext, PackageManager packageManager) { mRealContext = realContext; mPackageManager = packageManager; } /** * Resources. Pass through for now. */ @Override public Resources getResources() { return mRealContext.getResources(); } /** * Package manager. Pass through for now. */ @Override public PackageManager getPackageManager() { return mPackageManager; } /** * Package manager. Pass through for now. */ @Override public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException { return mRealContext.createPackageContext(packageName, flags); } /** * Message broadcast. Pass through for now. */ @Override public void sendBroadcast(Intent intent) { mRealContext.sendBroadcast(intent); } } /** * This is a mock for package manager. Used to perform a true unit test on SearchableInfo. * */ private class MyMockPackageManager extends MockPackageManager { public final static int SEARCHABLES_PASSTHROUGH = 0; public final static int SEARCHABLES_MOCK_ZERO = 1; public final static int SEARCHABLES_MOCK_ONEGOOD = 2; public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3; protected PackageManager mRealPackageManager; protected int mSearchablesMode; public MyMockPackageManager(PackageManager realPM) { mRealPackageManager = realPM; mSearchablesMode = SEARCHABLES_PASSTHROUGH; } /** * Set the mode for various tests. */ public void setSearchablesMode(int newMode) { switch (newMode) { case SEARCHABLES_PASSTHROUGH: case SEARCHABLES_MOCK_ZERO: mSearchablesMode = newMode; break; default: throw new UnsupportedOperationException(); } } /** * Find activities that support a given intent. * * Retrieve all activities that can be performed for the given intent. * * @param intent The desired intent as per resolveActivity(). * @param flags Additional option flags. The most important is * MATCH_DEFAULT_ONLY, to limit the resolution to only * those activities that support the CATEGORY_DEFAULT. * * @return A List<ResolveInfo> containing one entry for each matching * Activity. These are ordered from best to worst match -- that * is, the first item in the list is what is returned by * resolveActivity(). If there are no matching activities, an empty * list is returned. */ @Override public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { assertNotNull(intent); assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH) || intent.getAction().equals(Intent.ACTION_WEB_SEARCH) || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.queryIntentActivities(intent, flags); case SEARCHABLES_MOCK_ZERO: return null; default: throw new UnsupportedOperationException(); } } @Override public ResolveInfo resolveActivity(Intent intent, int flags) { assertNotNull(intent); assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH) || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.resolveActivity(intent, flags); case SEARCHABLES_MOCK_ZERO: return null; default: throw new UnsupportedOperationException(); } } /** * Retrieve an XML file from a package. This is a low-level API used to * retrieve XML meta data. * * @param packageName The name of the package that this xml is coming from. * Can not be null. * @param resid The resource identifier of the desired xml. Can not be 0. * @param appInfo Overall information about <var>packageName</var>. This * may be null, in which case the application information will be retrieved * for you if needed; if you already have this information around, it can * be much more efficient to supply it here. * * @return Returns an TypedXmlPullParser allowing you to parse out the XML * data. Returns null if the xml resource could not be found for any * reason. */ @Override public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) { assertNotNull(packageName); MoreAsserts.assertNotEqual(packageName, ""); MoreAsserts.assertNotEqual(resid, 0); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.getXml(packageName, resid, appInfo); case SEARCHABLES_MOCK_ZERO: default: throw new UnsupportedOperationException(); } } /** * Find a single content provider by its base path name. * * @param name The name of the provider to find. * @param flags Additional option flags. Currently should always be 0. * * @return ContentProviderInfo Information about the provider, if found, * else null. */ @Override public ProviderInfo resolveContentProvider(String name, int flags) { assertNotNull(name); MoreAsserts.assertNotEqual(name, ""); assertEquals(flags, 0); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.resolveContentProvider(name, flags); case SEARCHABLES_MOCK_ZERO: default: throw new UnsupportedOperationException(); } } /** * Get the activity information for a particular activity. * * @param name The name of the activity to find. * @param flags Additional option flags. * * @return ActivityInfo Information about the activity, if found, else null. */ @Override public ActivityInfo getActivityInfo(ComponentName name, int flags) throws NameNotFoundException { assertNotNull(name); MoreAsserts.assertNotEqual(name, ""); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.getActivityInfo(name, flags); case SEARCHABLES_MOCK_ZERO: throw new NameNotFoundException(); default: throw new UnsupportedOperationException(); } } @Override public int checkPermission(String permName, String pkgName) { assertNotNull(permName); assertNotNull(pkgName); switch (mSearchablesMode) { case SEARCHABLES_PASSTHROUGH: return mRealPackageManager.checkPermission(permName, pkgName); case SEARCHABLES_MOCK_ZERO: return PackageManager.PERMISSION_DENIED; default: throw new UnsupportedOperationException(); } } } }