1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.providers.applications; 18 19import android.app.SearchManager; 20import android.content.ComponentName; 21import android.content.Context; 22import android.content.pm.PackageManager; 23import android.database.Cursor; 24import android.net.Uri; 25import android.provider.Applications; 26import android.test.ProviderTestCase2; 27import android.test.suitebuilder.annotation.SmallTest; 28 29import java.util.concurrent.FutureTask; 30 31 32/** 33 * Instrumentation test for the ApplicationsProvider. 34 * 35 * The tests use an IsolatedContext, and are not affected by the real list of 36 * applications on the device. The ApplicationsProvider's persistent database 37 * is also created in an isolated context so it doesn't interfere with the 38 * database of the actual ApplicationsProvider installed on the device. 39 */ 40@SmallTest 41public class ApplicationsProviderTest extends ProviderTestCase2<ApplicationsProviderForTesting> { 42 43 private ApplicationsProviderForTesting mProvider; 44 45 private MockActivityManager mMockActivityManager; 46 47 public ApplicationsProviderTest() { 48 super(ApplicationsProviderForTesting.class, Applications.AUTHORITY); 49 } 50 51 @Override 52 protected void setUp() throws Exception { 53 super.setUp(); 54 mProvider = getProvider(); 55 mMockActivityManager = new MockActivityManager(); 56 } 57 58 /** 59 * Ensures that the ApplicationsProvider is in a ready-to-test state. 60 */ 61 private void initProvider(ApplicationsProviderForTesting provider) throws Exception { 62 // Decouple the provider from Android's real list of applications. 63 MockPackageManager mockPackageManager = new MockPackageManager(); 64 addDefaultTestPackages(mockPackageManager); 65 66 initProvider(provider, mockPackageManager); 67 } 68 69 private void initProvider(ApplicationsProviderForTesting provider, 70 MockPackageManager mockPackageManager) throws Exception { 71 provider.setMockPackageManager(mockPackageManager); 72 provider.setMockActivityManager(mMockActivityManager); 73 74 assertTrue(provider.dispatchNextMessage()); 75 } 76 77 /** 78 * Register a few default applications with the ApplicationsProvider that 79 * tests can query. 80 */ 81 private void addDefaultTestPackages(MockPackageManager mockPackageManager) { 82 mockPackageManager.addPackage( 83 "Email", new ComponentName("com.android.email", "com.android.email.MainView")); 84 mockPackageManager.addPackage( 85 "Ebay", new ComponentName("com.android.ebay", "com.android.ebay.Shopping")); 86 mockPackageManager.addPackage( 87 "Fakeapp", new ComponentName("com.android.fakeapp", "com.android.fakeapp.FakeView")); 88 89 // Apps that can be used to test ordering. 90 mockPackageManager.addPackage("AlphabeticA", new ComponentName("a", "a.AView")); 91 mockPackageManager.addPackage("AlphabeticB", new ComponentName("b", "b.BView")); 92 mockPackageManager.addPackage("AlphabeticC", new ComponentName("c", "c.CView")); 93 mockPackageManager.addPackage("AlphabeticD", new ComponentName("d", "d.DView")); 94 mockPackageManager.addPackage("AlphabeticD2", new ComponentName("d", "d.DView2")); 95 } 96 97 public void testSearch_singleResult() throws Exception { 98 initProvider(mProvider); 99 testSearch("ema", "Email"); 100 } 101 102 public void testSearch_multipleResults() throws Exception { 103 initProvider(mProvider); 104 testSearch("e", "Ebay", "Email"); 105 } 106 107 public void testSearch_noResults() throws Exception { 108 initProvider(mProvider); 109 testSearch("nosuchapp"); 110 } 111 112 public void testSearch_orderingIsAlphabeticByDefault() throws Exception { 113 initProvider(mProvider); 114 testSearch("alphabetic", "AlphabeticA", "AlphabeticB", "AlphabeticC", "AlphabeticD", 115 "AlphabeticD2"); 116 } 117 118 public void testSearch_emptySearchQueryReturnsEverything() throws Exception { 119 initProvider(mProvider); 120 testSearch("", 121 "AlphabeticA", "AlphabeticB", "AlphabeticC", "AlphabeticD", "AlphabeticD2", 122 "Ebay", "Email", "Fakeapp"); 123 } 124 125 public void testSearch_appsAreRankedByUsageTimeOnStartup() throws Exception { 126 mMockActivityManager.addLastResumeTime("d", "d.DView", 3); 127 mMockActivityManager.addLastResumeTime("b", "b.BView", 1); 128 // Missing usage time for "a". 129 mMockActivityManager.addLastResumeTime("c", "c.CView", 0); 130 131 // Launch count database is populated on startup. 132 mProvider.setHasGlobalSearchPermission(true); 133 134 initProvider(mProvider); 135 136 // New ranking: D, B, A, C (first by launch count, then 137 // - if the launch counts of two apps are equal - alphabetically) 138 testSearch("alphabetic", "AlphabeticD", "AlphabeticB", "AlphabeticA", "AlphabeticC", 139 "AlphabeticD2"); 140 } 141 142 public void testSearch_appsAreRankedByResumeTimeAfterUpdate() throws Exception { 143 initProvider(mProvider); 144 mProvider.setHasGlobalSearchPermission(true); 145 146 mMockActivityManager.addLastResumeTime("d", "d.DView", 3); 147 mMockActivityManager.addLastResumeTime("b", "b.BView", 1); 148 // Missing launch count for "a". 149 mMockActivityManager.addLastResumeTime("c", "c.CView", 0); 150 151 // Fetch new data from usage stat provider (in the real instance this 152 // is triggered by a zero-query from global search). 153 mProvider.updateUsageStats(); 154 155 // New ranking: D, B, A, C (first by launch count, then 156 // - if the launch counts of two apps are equal - alphabetically) 157 testSearch("alphabetic", "AlphabeticD", "AlphabeticB", "AlphabeticA", "AlphabeticC", 158 "AlphabeticD2"); 159 } 160 161 public void testSearch_noLastAccessTimesWithoutPermission() throws Exception { 162 initProvider(mProvider); 163 mProvider.setHasGlobalSearchPermission(false); 164 mMockActivityManager.addLastResumeTime("d", "d.DView", 1); 165 mMockActivityManager.addLastResumeTime("b", "b.BView", 2); 166 mMockActivityManager.addLastResumeTime("c", "c.CView", 3); 167 168 assertNull(getGlobalSearchCursor("", false)); 169 170 Cursor cursor = getGlobalSearchCursor("alphabetic", false); 171 assertEquals(-1, cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT)); 172 } 173 174 public void testSearch_lastAccessTimes() throws Exception { 175 initProvider(mProvider); 176 mProvider.setHasGlobalSearchPermission(true); 177 mMockActivityManager.addLastResumeTime("d", "d.DView", 1); 178 mMockActivityManager.addLastResumeTime("b", "b.BView", 2); 179 mMockActivityManager.addLastResumeTime("c", "c.CView", 3); 180 181 testLastAccessTimes("", true, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0); 182 183 testLastAccessTimes("alphabetic", true, 3, 2, 1, 0, 0); 184 185 // Update the last resume time of "c". 186 mMockActivityManager.addLastResumeTime("c", "c.CView", 5); 187 // Without a refresh, we should see the same results as before. 188 testLastAccessTimes("alphabetic", false, 3, 2, 1, 0, 0); 189 // If we refresh, we should see the change. 190 testLastAccessTimes("alphabetic", true, 5, 2, 1, 0, 0); 191 } 192 193 /** 194 * The ApplicationsProvider must only rank by launch count if the caller 195 * is a privileged application - ordering apps by launch count when asked 196 * by a regular application would leak information about user behavior. 197 */ 198 public void testSearch_notAllowedToRankByLaunchCount() throws Exception { 199 initProvider(mProvider); 200 // Simulate non-privileged calling application. 201 mProvider.setHasGlobalSearchPermission(false); 202 203 mMockActivityManager.addLastResumeTime("d", "d.DView", 3); 204 mMockActivityManager.addLastResumeTime("b", "b.BView", 1); 205 mMockActivityManager.addLastResumeTime("a", "a.AView", 0); 206 mMockActivityManager.addLastResumeTime("c", "c.CView", 0); 207 208 // Fetch new data from launch count provider. 209 mProvider.updateUsageStats(); 210 211 // Launch count information mustn't be leaked - ranking is still 212 // alphabetic. 213 testSearch("alphabetic", "AlphabeticA", "AlphabeticB", "AlphabeticC", "AlphabeticD", 214 "AlphabeticD2"); 215 } 216 217 public void testSearch_disabledPackage() throws Exception { 218 MockPackageManager mockPackageManager = new MockPackageManager(); 219 mockPackageManager.addPackage("DisabledPackageApp1", 220 new ComponentName("dp", "dp.DisView1")); 221 mockPackageManager.addPackage("DisabledPackageApp2", 222 new ComponentName("dp", "dp.DisView2"), 223 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 224 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); 225 initProvider(mProvider, mockPackageManager); 226 227 mProvider.setHasGlobalSearchPermission(true); 228 testSearch("dis"); 229 } 230 231 public void testSearch_disabledComponent() throws Exception { 232 MockPackageManager mockPackageManager = new MockPackageManager(); 233 mockPackageManager.addPackage("DisabledApp1", new ComponentName("da", "da.DaView1"), 234 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 235 PackageManager.COMPONENT_ENABLED_STATE_DISABLED); 236 mockPackageManager.addPackage("DisabledApp2", new ComponentName("da", "da.DaView2"), 237 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 238 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); 239 initProvider(mProvider, mockPackageManager); 240 241 mProvider.setHasGlobalSearchPermission(true); 242 testSearch("dis", "DisabledApp2"); 243 } 244 245 private void testSearch(String searchQuery, String... expectedResultsInOrder) { 246 Cursor cursor = Applications.search(getMockContentResolver(), searchQuery); 247 248 assertNotNull(cursor); 249 assertFalse(cursor.isClosed()); 250 251 verifySearchResults(cursor, expectedResultsInOrder); 252 253 cursor.close(); 254 } 255 256 private void verifySearchResults(Cursor cursor, String... expectedResultsInOrder) { 257 int expectedResultCount = expectedResultsInOrder.length; 258 assertEquals("Wrong number of app search results.", 259 expectedResultCount, cursor.getCount()); 260 261 if (expectedResultCount > 0) { 262 cursor.moveToFirst(); 263 int nameIndex = cursor.getColumnIndex(ApplicationsProvider.NAME); 264 // Verify that the actual results match the expected ones. 265 for (int i = 0; i < cursor.getCount(); i++) { 266 assertEquals("Wrong search result at position " + i, 267 expectedResultsInOrder[i], cursor.getString(nameIndex)); 268 cursor.moveToNext(); 269 } 270 } 271 } 272 273 private Cursor getGlobalSearchCursor(String searchQuery, boolean refresh) { 274 Uri.Builder uriBuilder = Applications.CONTENT_URI.buildUpon(); 275 uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY).appendPath(searchQuery); 276 if (refresh) { 277 uriBuilder.appendQueryParameter(ApplicationsProvider.REFRESH_STATS, ""); 278 } 279 return getMockContentResolver().query(uriBuilder.build(), null, null, null, null); 280 } 281 282 private void testLastAccessTimes(String searchQuery, boolean refresh, 283 int... expectedLastAccessTimesInOrder) { 284 Cursor cursor = getGlobalSearchCursor(searchQuery, refresh); 285 286 assertNotNull(cursor); 287 assertFalse(cursor.isClosed()); 288 289 verifyLastAccessTimes(cursor, expectedLastAccessTimesInOrder); 290 291 cursor.close(); 292 } 293 294 private void verifyLastAccessTimes(Cursor cursor, int... expectedLastAccessTimesInOrder) { 295 cursor.moveToFirst(); 296 int lastAccessTimeIndex = cursor.getColumnIndex( 297 SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT); 298 // Verify that the actual results match the expected ones. 299 for (int i = 0; i < cursor.getCount(); i++) { 300 assertEquals("Wrong last-access time at position " + i, 301 expectedLastAccessTimesInOrder[i], cursor.getInt(lastAccessTimeIndex)); 302 cursor.moveToNext(); 303 } 304 } 305} 306