diff options
author | Peter Visontay <pvisontay@google.com> | 2011-02-11 18:36:18 +0000 |
---|---|---|
committer | Peter Visontay <pvisontay@google.com> | 2011-02-25 12:03:19 +0000 |
commit | 7089c89c9ec21f28326ac6a9e9e91344749a02e0 (patch) | |
tree | 205f615b265a66bf30933bc14afc3265c93264bb | |
parent | d995cd1f165074ebf14b9b884486770f2c219499 (diff) | |
download | ApplicationsProvider-7089c89c9ec21f28326ac6a9e9e91344749a02e0.tar.gz |
Made ApplicationsProvider fetch launch counts from a remote service.
Bug: 3431684
Change-Id: Ie350ef440a7a49e5fdae5723414bfbab11f09793
9 files changed, 182 insertions, 374 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3cdaaa9..4e4bfa2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,12 +2,7 @@ package="com.android.providers.applications" android:sharedUserId="android.uid.shared"> - <!-- Allows the system to register app launches so the - ApplicationsProvider can also rank apps by frequency of use. --> - <permission android:name="com.google.android.providers.applications.permission.INCREASE_LAUNCH_COUNT" - android:protectionLevel="dangerous" - android:label="@string/permlab_increase_launch_count" - android:description="@string/permdesc_increase_launch_count" /> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> <application android:process="android.process.acore" android:label="@string/app_label"> diff --git a/res/values/strings.xml b/res/values/strings.xml index 6974f2c..2c296ef 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -28,11 +28,4 @@ settings. --> <string name="settings_description">Names of installed applications</string> - <!-- Label for permission to increase an app's launch count. --> - <string name="permlab_increase_launch_count">modify application launch counts</string> - - <!-- Description of permission to increase an app's launch count. --> - <string name="permdesc_increase_launch_count">Allows a launcher to - increase the launch count of applications (used for ranking apps).</string> - </resources> diff --git a/src/com/android/providers/applications/ApplicationLauncher.java b/src/com/android/providers/applications/ApplicationLauncher.java index 343c855..0b38dfe 100644 --- a/src/com/android/providers/applications/ApplicationLauncher.java +++ b/src/com/android/providers/applications/ApplicationLauncher.java @@ -23,7 +23,6 @@ import android.content.ComponentName; import android.content.Intent; import android.database.Cursor; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.provider.Applications; import android.util.Log; @@ -99,22 +98,6 @@ public class ApplicationLauncher extends ListActivity { } catch (ActivityNotFoundException ex) { Log.w(TAG, "Activity not found: " + componentName); } - - increaseLaunchCount(componentName); } } - - private void increaseLaunchCount(final ComponentName componentName) { - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... args) { - try { - Applications.increaseLaunchCount(getContentResolver(), componentName); - } catch (RuntimeException e) { - Log.w(TAG, "Could not increase launch count", e); - } - return null; - } - }.execute(); - } } diff --git a/src/com/android/providers/applications/ApplicationsProvider.java b/src/com/android/providers/applications/ApplicationsProvider.java index fbca4cb..e46aa52 100644 --- a/src/com/android/providers/applications/ApplicationsProvider.java +++ b/src/com/android/providers/applications/ApplicationsProvider.java @@ -18,6 +18,9 @@ package com.android.providers.applications; import com.android.internal.content.PackageMonitor; +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.app.PendingIntent; import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -74,7 +77,6 @@ public class ApplicationsProvider extends ContentProvider { private static final int SEARCH_SUGGEST = 0; private static final int SHORTCUT_REFRESH = 1; private static final int SEARCH = 2; - private static final int LAUNCH_COUNT_INCREASE = 3; private static final UriMatcher sURIMatcher = buildUriMatcher(); @@ -82,6 +84,11 @@ public class ApplicationsProvider extends ContentProvider { // Messages for mHandler private static final int MSG_UPDATE_ALL = 0; + private static final int MSG_UPDATE_APP_LAUNCH_COUNTS = 1; + + // A request to update application launch counts. + private static final String INTENT_UPDATE_LAUNCH_COUNTS = + ApplicationsProvider.class.getName() + ".UPDATE_LAUNCH_COUNTS"; public static final String _ID = "_id"; public static final String NAME = "name"; @@ -97,12 +104,6 @@ public class ApplicationsProvider extends ContentProvider { "applicationsLookup JOIN " + APPLICATIONS_TABLE + " ON" + " applicationsLookup.source = " + APPLICATIONS_TABLE + "." + _ID; - private static final String INCREASE_LAUNCH_COUNT_SQL = - "UPDATE " + APPLICATIONS_TABLE + - " SET " + LAUNCH_COUNT + " = " + LAUNCH_COUNT + " + 1" + - " WHERE " + PACKAGE + " = ?" + - " AND " + CLASS + " = ?"; - private static final HashMap<String, String> sSearchSuggestionsProjectionMap = buildSuggestionsProjectionMap(); private static final HashMap<String, String> sSearchProjectionMap = @@ -111,13 +112,9 @@ public class ApplicationsProvider extends ContentProvider { /** * An in-memory database storing the details of applications installed on * the device. Populated when the ApplicationsProvider is launched. - * - * @see PersistentDb */ private SQLiteDatabase mDb; - private PersistentDb mPersistentDb; - // Handler that runs DB updates. private Handler mHandler; @@ -129,6 +126,11 @@ public class ApplicationsProvider extends ContentProvider { */ private static final long UPDATE_DELAY_MILLIS = 1000L; + /** + * Application launch counts will be updated every 6 hours. + */ + private static final long LAUNCH_COUNT_UPDATE_INTERVAL = AlarmManager.INTERVAL_HOUR * 6; + private static UriMatcher buildUriMatcher() { UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); matcher.addURI(Applications.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, @@ -143,8 +145,6 @@ public class ApplicationsProvider extends ContentProvider { SEARCH); matcher.addURI(Applications.AUTHORITY, Applications.SEARCH_PATH + "/*", SEARCH); - matcher.addURI(Applications.AUTHORITY, Applications.INCREASE_LAUNCH_COUNT_PATH, - LAUNCH_COUNT_INCREASE); return matcher; } @@ -172,10 +172,23 @@ public class ApplicationsProvider extends ContentProvider { } }; + // Broadcast receiver receiving "update application launch counts" requests + // fired by the AlarmManager at regular intervals. + private BroadcastReceiver mLaunchCountUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (INTENT_UPDATE_LAUNCH_COUNTS.equals(action)) { + if (DBG) Log.d(TAG, "Launch count update requested"); + mHandler.removeMessages(MSG_UPDATE_APP_LAUNCH_COUNTS); + Message.obtain(mHandler, MSG_UPDATE_APP_LAUNCH_COUNTS).sendToTarget(); + } + } + }; + @Override public boolean onCreate() { createDatabase(); - createPersistentDatabase(); // Listen for package changes new MyPackageMonitor().register(getContext(), true); // Listen for locale changes @@ -187,6 +200,7 @@ public class ApplicationsProvider extends ContentProvider { mHandler = new UpdateHandler(thread.getLooper()); // Kick off first apps update postUpdateAll(); + scheduleRegularLaunchCountUpdates(); return true; } @@ -202,6 +216,9 @@ public class ApplicationsProvider extends ContentProvider { case MSG_UPDATE_ALL: updateApplicationsList(null); break; + case MSG_UPDATE_APP_LAUNCH_COUNTS: + updateLaunchCounts(); + break; default: Log.e(TAG, "Unknown message: " + msg.what); break; @@ -221,6 +238,27 @@ public class ApplicationsProvider extends ContentProvider { mHandler.sendMessageDelayed(msg, UPDATE_DELAY_MILLIS); } + @VisibleForTesting + protected void scheduleRegularLaunchCountUpdates() { + // Set up a recurring event that sends an intent caught by the + // mLaunchCountUpdateReceiver event handler. This event handler + // will update application launch counts in the ApplicationsProvider's + // database. + getContext().registerReceiver( + mLaunchCountUpdateReceiver, + new IntentFilter(INTENT_UPDATE_LAUNCH_COUNTS)); + + PendingIntent updateLaunchCountsIntent = PendingIntent.getBroadcast( + getContext(), 0, new Intent(INTENT_UPDATE_LAUNCH_COUNTS), + PendingIntent.FLAG_CANCEL_CURRENT); + + // Schedule the recurring event. + AlarmManager alarmManager = + (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); + alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, LAUNCH_COUNT_UPDATE_INTERVAL, + LAUNCH_COUNT_UPDATE_INTERVAL, updateLaunchCountsIntent); + } + // ---------- // END ASYC UPDATE CODE // ---------- @@ -273,15 +311,6 @@ public class ApplicationsProvider extends ContentProvider { } /** - * Creates a persistent database that complements the in-memory one by - * storing values that must be kept even if the ApplicationsProvider is - * restarted. - */ - private void createPersistentDatabase() { - mPersistentDb = new PersistentDb(getContext()); - } - - /** * This will always return {@link SearchManager#SUGGEST_MIME_TYPE} as this * provider is purely to provide suggestions. */ @@ -474,10 +503,7 @@ public class ApplicationsProvider extends ContentProvider { int iconCol = inserter.getColumnIndex(ICON); int launchCountCol = inserter.getColumnIndex(LAUNCH_COUNT); - // We'll copy the values stored in the persistent DB to the in-memory - // one to improve querying speed. - Map<ComponentName, Long> launchCounts = mPersistentDb.getLaunchCounts(); - List<ComponentName> newComponents = new ArrayList<ComponentName>(); + Map<String, Integer> launchCounts = fetchLaunchCounts(); mDb.beginTransaction(); try { @@ -502,12 +528,9 @@ public class ApplicationsProvider extends ContentProvider { } String activityPackageName = info.activityInfo.applicationInfo.packageName; - ComponentName componentName = new ComponentName(activityPackageName, activityClassName); - Long launchCount = launchCounts.get(componentName); + Integer launchCount = launchCounts.get(activityPackageName); if (launchCount == null) { - // Component not in the persisted DB yet. - newComponents.add(componentName); - launchCount = 0L; + launchCount = 0; } String icon = getActivityIconUri(info.activityInfo); @@ -526,11 +549,6 @@ public class ApplicationsProvider extends ContentProvider { inserter.close(); } - // Add new components to persistent DB. - for (ComponentName newComponent : newComponents) { - mPersistentDb.insertNewComponent(newComponent); - } - if (onApplicationsListUpdated != null) { onApplicationsListUpdated.run(); } @@ -538,6 +556,27 @@ public class ApplicationsProvider extends ContentProvider { if (DBG) Log.d(TAG, "Finished updating database."); } + @VisibleForTesting + protected void updateLaunchCounts() { + Map<String, Integer> launchCounts = fetchLaunchCounts(); + + mDb.beginTransaction(); + try { + for (String packageName : launchCounts.keySet()) { + ContentValues updatedValues = new ContentValues(); + updatedValues.put(LAUNCH_COUNT, launchCounts.get(packageName)); + + mDb.update(APPLICATIONS_TABLE, updatedValues, + PACKAGE + " = ?", new String[] { packageName }); + } + mDb.setTransactionSuccessful(); + } finally { + mDb.endTransaction(); + } + + if (DBG) Log.d(TAG, "Finished updating application launch counts in database."); + } + private String getActivityIconUri(ActivityInfo activityInfo) { int icon = activityInfo.getIconResource(); if (icon == 0) return null; @@ -556,61 +595,7 @@ public class ApplicationsProvider extends ContentProvider { @Override public Uri insert(Uri uri, ContentValues values) { - if (DBG) Log.d(TAG, "Data insert request arrived on uri " - + uri + " with parameters: " + values); - - switch (sURIMatcher.match(uri)) { - case LAUNCH_COUNT_INCREASE: - increaseLaunchCount(values); - return null; - - default: - throw new IllegalArgumentException("URL " + uri + " doesn't support inserts."); - } - } - - private void increaseLaunchCount(ContentValues requestParameters) { - String packageName = requestParameters.getAsString( - Applications.INCREASE_LAUNCH_COUNT_PACKAGE); - String className = requestParameters.getAsString( - Applications.INCREASE_LAUNCH_COUNT_CLASS); - - if (TextUtils.isEmpty(packageName)) { - Log.w(TAG, Applications.INCREASE_LAUNCH_COUNT_PACKAGE + " parameter missing"); - return; - } - - if (TextUtils.isEmpty(className)) { - Log.w(TAG, Applications.INCREASE_LAUNCH_COUNT_CLASS + " parameter missing"); - return; - } - - ComponentName componentName = new ComponentName(packageName, className); - increaseLaunchCount(componentName); - } - - private void increaseLaunchCount(ComponentName componentName) { - enforcePermissionForLaunchCountIncrease(); - - if (DBG) Log.d(TAG, "Increasing launch count of component " + componentName); - - // Increase the launch count in the in-memory database. - mDb.beginTransaction(); - try { - mDb.execSQL(INCREASE_LAUNCH_COUNT_SQL, new Object[] { - componentName.getPackageName(), - componentName.getClassName()}); - - mDb.setTransactionSuccessful(); - } finally { - mDb.endTransaction(); - } - - // Also persist the new value since the ApplicationsProvider may be - // killed unexpectedly. - mPersistentDb.increaseLaunchCount(componentName); - - if (DBG) Log.d(TAG, "Launch count increased for component " + componentName); + throw new UnsupportedOperationException(); } @Override @@ -658,14 +643,22 @@ public class ApplicationsProvider extends ContentProvider { } @VisibleForTesting - protected PackageManager getPackageManager() { - return getContext().getPackageManager(); + protected Map<String, Integer> fetchLaunchCounts() { + try { + ActivityManager activityManager = (ActivityManager) + getContext().getSystemService(Context.ACTIVITY_SERVICE); + + Map<String, Integer> allPackageLaunchCounts = activityManager.getAllPackageLaunchCounts(); + return allPackageLaunchCounts; + } catch (Exception e) { + Log.w(TAG, "Could not fetch launch counts", e); + return new HashMap<String, Integer>(); + } } @VisibleForTesting - protected void enforcePermissionForLaunchCountIncrease() { - getContext().enforceCallingOrSelfPermission( - Manifest.permission.INCREASE_LAUNCH_COUNT, null); + protected PackageManager getPackageManager() { + return getContext().getPackageManager(); } @VisibleForTesting diff --git a/src/com/android/providers/applications/PersistentDb.java b/src/com/android/providers/applications/PersistentDb.java deleted file mode 100644 index 3394369..0000000 --- a/src/com/android/providers/applications/PersistentDb.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2010 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.providers.applications; - -import android.content.ComponentName; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; - - -/** - * Manages a database that stores those details of applications that are - * managed by the ApplicationsProvider. As these properties are not retrievable - * from an external service and therefore have to be stored in a persistent - * database. - * - * Currently used to store the launch counts of applications to improve ranking - * by popularity. - */ -class PersistentDb { - - private static final boolean DBG = false; - - private static final String TAG = "ApplicationsProvider"; - - private static final int DB_VERSION = 1; - - // Table names and constants. - private static final String APPLICATIONS_TABLE = "applications"; - public static final String PACKAGE = "package"; - public static final String CLASS = "class"; - public static final String LAUNCH_COUNT = "launch_count"; - - private static final String INCREASE_LAUNCH_COUNT_SQL = - "UPDATE " + APPLICATIONS_TABLE + - " SET " + LAUNCH_COUNT + " = " + LAUNCH_COUNT + " + 1" + - " WHERE " + PACKAGE + " = ?" + - " AND " + CLASS + " = ?"; - - private SQLiteDatabase mDb; - - public PersistentDb(Context context) { - openDatabase(context); - } - - private void openDatabase(Context context) { - try { - mDb = new DbOpenHelper(context, DB_VERSION).getWritableDatabase(); - if (DBG) Log.d(TAG, "Opened persistent ApplicationsProvider database"); - } catch (SQLiteException e) { - Log.w(TAG, "Could not open persistent ApplicationsProvider database", e); - } - } - - /** - * Returns the persisted launch count of each component. - */ - Map<ComponentName, Long> getLaunchCounts() { - Cursor cursor = null; - try { - cursor = mDb.query( - APPLICATIONS_TABLE, - new String[] { PACKAGE, CLASS, LAUNCH_COUNT }, - null, null, null, null, null); - } catch (SQLiteException e) { - Log.w(TAG, "Could not query persistent database.", e); - return new HashMap<ComponentName, Long>(); - } - - try { - int packageCol = cursor.getColumnIndex(PACKAGE); - int classCol = cursor.getColumnIndex(CLASS); - int launchCountCol = cursor.getColumnIndex(LAUNCH_COUNT); - - HashMap<ComponentName, Long> packageLaunchCounts = new HashMap<ComponentName, Long>(); - while (cursor.moveToNext()) { - ComponentName componentName = new ComponentName( - cursor.getString(packageCol), - cursor.getString(classCol)); - - packageLaunchCounts.put( - componentName, - cursor.getLong(launchCountCol)); - } - return packageLaunchCounts; - } finally { - if (cursor != null && !cursor.isClosed()) { - cursor.close(); - } - } - } - - /** - * Add a new component to the database with a zero launch count. - */ - void insertNewComponent(ComponentName component) { - if (DBG) Log.d(TAG, "Adding component " + component + " to persistent database."); - try { - mDb.beginTransaction(); - try { - ContentValues columns = new ContentValues(); - columns.put(PACKAGE, component.getPackageName()); - columns.put(CLASS, component.getClassName()); - columns.put(LAUNCH_COUNT, 0); - - mDb.insert(APPLICATIONS_TABLE, null, columns); - mDb.setTransactionSuccessful(); - } finally { - mDb.endTransaction(); - } - } catch (SQLiteException e) { - Log.w(TAG, "Could not add component " + component + " to persistent database.", e); - } - } - - /** - * Increase the launch count of a single package. - */ - void increaseLaunchCount(ComponentName componentName) { - try { - mDb.beginTransaction(); - try { - mDb.execSQL(INCREASE_LAUNCH_COUNT_SQL, new Object[] { - componentName.getPackageName(), - componentName.getClassName()}); - - mDb.setTransactionSuccessful(); - } finally { - mDb.endTransaction(); - } - } catch (SQLiteException e) { - Log.w(TAG, "Could not increase persisted launch count of " + componentName, e); - } - } - - private class DbOpenHelper extends SQLiteOpenHelper { - - private static final String DB_NAME = "applicationsprovider.db"; - - public DbOpenHelper(Context context, int version) { - super(context, DB_NAME, null, version); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - throw new UnsupportedOperationException("ApplicationsProvider DB cannot" - + " require an upgrade - only one version exists so far."); - } - - @Override - public void onCreate(SQLiteDatabase db) { - if (DBG) Log.d(TAG, "Creating persistent database for ApplicationsProvider."); - - db.execSQL("CREATE TABLE " + APPLICATIONS_TABLE + " (" + - PACKAGE + " TEXT, " + - CLASS + " TEXT, " + - LAUNCH_COUNT + " INTEGER DEFAULT 0" + - ");"); - - db.execSQL("CREATE INDEX componentNameIndex ON " + APPLICATIONS_TABLE + " (" - + PACKAGE + "," - + CLASS - + ");"); - } - } -} diff --git a/tests/runtests b/tests/runtests index 184a278..be1c779 100644 --- a/tests/runtests +++ b/tests/runtests @@ -1,4 +1,5 @@ mmm packages/providers/ApplicationsProvider || return +mmm packages/providers/ApplicationsProvider/tests || return adb install -r ${OUT}/system/app/ApplicationsProvider.apk || return adb install -r ${OUT}/data/app/ApplicationsProviderTests.apk || return adb shell am instrument -w -e class com.android.providers.applications.ApplicationsProviderTest com.android.providers.applications.tests/android.test.InstrumentationTestRunner || return diff --git a/tests/src/com/android/providers/applications/ApplicationsProviderForTesting.java b/tests/src/com/android/providers/applications/ApplicationsProviderForTesting.java index ea91fd0..fa8645c 100644 --- a/tests/src/com/android/providers/applications/ApplicationsProviderForTesting.java +++ b/tests/src/com/android/providers/applications/ApplicationsProviderForTesting.java @@ -18,6 +18,8 @@ package com.android.providers.applications; import android.content.pm.PackageManager; +import java.util.Map; + /** * An extension of {@link ApplicationsProvider} that makes its testing easier. */ @@ -25,6 +27,8 @@ public class ApplicationsProviderForTesting extends ApplicationsProvider { private PackageManager mMockPackageManager; + private MockActivityManager mMockActivityManager; + private boolean mCanRankByLaunchCount; @Override @@ -37,8 +41,12 @@ public class ApplicationsProviderForTesting extends ApplicationsProvider { } @Override - protected void enforcePermissionForLaunchCountIncrease() { - // Tests are allowed to do this. + protected Map<String, Integer> fetchLaunchCounts() { + return mMockActivityManager.getAllPackageLaunchCounts(); + } + + protected void setMockActivityManager(MockActivityManager mockActivityManager) { + mMockActivityManager = mockActivityManager; } protected void setCanRankByLaunchCount(boolean canRankByLaunchCount) { @@ -49,4 +57,8 @@ public class ApplicationsProviderForTesting extends ApplicationsProvider { protected boolean canRankByLaunchCount() { return mCanRankByLaunchCount; } + + @Override + protected void scheduleRegularLaunchCountUpdates() { + } } diff --git a/tests/src/com/android/providers/applications/ApplicationsProviderTest.java b/tests/src/com/android/providers/applications/ApplicationsProviderTest.java index 9d07bb2..77bc064 100644 --- a/tests/src/com/android/providers/applications/ApplicationsProviderTest.java +++ b/tests/src/com/android/providers/applications/ApplicationsProviderTest.java @@ -17,17 +17,13 @@ package com.android.providers.applications; import android.content.ComponentName; -import android.content.ContentValues; import android.content.Context; import android.database.Cursor; -import android.net.Uri; import android.provider.Applications; import android.test.ProviderTestCase2; import android.test.suitebuilder.annotation.LargeTest; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.FutureTask; -import java.util.concurrent.TimeUnit; /** @@ -43,6 +39,8 @@ public class ApplicationsProviderTest extends ProviderTestCase2<ApplicationsProv private ApplicationsProviderForTesting mProvider; + private MockActivityManager mMockActivityManager; + public ApplicationsProviderTest() { super(ApplicationsProviderForTesting.class, Applications.AUTHORITY); } @@ -51,6 +49,7 @@ public class ApplicationsProviderTest extends ProviderTestCase2<ApplicationsProv protected void setUp() throws Exception { super.setUp(); mProvider = getProvider(); + mMockActivityManager = new MockActivityManager(); initProvider(mProvider); } @@ -62,6 +61,7 @@ public class ApplicationsProviderTest extends ProviderTestCase2<ApplicationsProv MockPackageManager mockPackageManager = new MockPackageManager(); addDefaultTestPackages(mockPackageManager); 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 @@ -118,14 +118,37 @@ public class ApplicationsProviderTest extends ProviderTestCase2<ApplicationsProv "Ebay", "Email", "Fakeapp"); } - public void testSearch_appsAreRankedByLaunchCount() { + public void testSearch_appsAreRankedByLaunchCountOnStartup() throws Exception { + mMockActivityManager.addLaunchCount("d", 3); + mMockActivityManager.addLaunchCount("b", 1); + // Missing launch count for "a". + mMockActivityManager.addLaunchCount("c", 0); + + // Launch count database is populated on startup. + mProvider = createNewProvider(getMockContext()); + mProvider.setCanRankByLaunchCount(true); + initProvider(mProvider); + + // Override the previous provider with the new instance in the + // ContentResolver. + getMockContentResolver().addProvider(Applications.AUTHORITY, mProvider); + + // New ranking: D, B, A, C (first by launch count, then + // - if the launch counts of two apps are equal - alphabetically) + testSearch("alphabetic", "AlphabeticD", "AlphabeticB", "AlphabeticA", "AlphabeticC"); + } + + public void testSearch_appsAreRankedByLaunchCountAfterScheduledUpdate() { mProvider.setCanRankByLaunchCount(true); - // Original ranking: A, B, C, D (alphabetic; all launch counts are 0 - // by default). - increaseLaunchCount(new ComponentName("b", "b.BView")); - increaseLaunchCount(new ComponentName("d", "d.DView")); - increaseLaunchCount(new ComponentName("d", "d.DView")); + mMockActivityManager.addLaunchCount("d", 3); + mMockActivityManager.addLaunchCount("b", 1); + // Missing launch count for "a". + mMockActivityManager.addLaunchCount("c", 0); + + // Fetch new data from launch count provider (in the real instance this + // is scheduled). + mProvider.updateLaunchCounts(); // New ranking: D, B, A, C (first by launch count, then // - if the launch counts of two apps are equal - alphabetically) @@ -141,52 +164,19 @@ public class ApplicationsProviderTest extends ProviderTestCase2<ApplicationsProv // Simulate non-privileged calling application. mProvider.setCanRankByLaunchCount(false); - // Original ranking: A, B, C, D (alphabetic; all launch counts are 0 - // by default). - increaseLaunchCount(new ComponentName("b", "b.BView")); - increaseLaunchCount(new ComponentName("d", "d.DView")); - increaseLaunchCount(new ComponentName("d", "d.DView")); + mMockActivityManager.addLaunchCount("d", 3); + mMockActivityManager.addLaunchCount("b", 1); + mMockActivityManager.addLaunchCount("a", 0); + mMockActivityManager.addLaunchCount("c", 0); + + // Fetch new data from launch count provider. + mProvider.updateLaunchCounts(); // Launch count information mustn't be leaked - ranking is still // alphabetic. testSearch("alphabetic", "AlphabeticA", "AlphabeticB", "AlphabeticC", "AlphabeticD"); } - /** - * Tests that the launch count values are persisted even if the - * ApplicationsProvider is restarted. - */ - public void testSearch_launchCountInformationIsPersistent() throws Exception { - mProvider.setCanRankByLaunchCount(true); - - // Original ranking: A, B, C, D (alphabetic; all launch counts are 0 - // by default). - increaseLaunchCount(new ComponentName("b", "b.BView")); - increaseLaunchCount(new ComponentName("d", "d.DView")); - increaseLaunchCount(new ComponentName("d", "d.DView")); - - // New ranking: D, B, A, C (first by launch count, then - // - if the launch counts of two apps are equal - alphabetically) - testSearch("alphabetic", "AlphabeticD", "AlphabeticB", "AlphabeticA", "AlphabeticC"); - - // Now we'll create a new ApplicationsProvider instance (the provider - // may be killed by Android at any time) and verify that it has access - // to the same launch count information as the original provider instance. - // The new instance will use the same IsolatedContext as the previous one. - ApplicationsProviderForTesting newProviderInstance = createNewProvider(mProvider.getContext()); - newProviderInstance.setCanRankByLaunchCount(true); - assertNotSame(newProviderInstance, mProvider); - - // Override the previous provider with the new instance in the - // ContentResolver. - getMockContentResolver().addProvider(Applications.AUTHORITY, newProviderInstance); - - initProvider(newProviderInstance); - - // Verify that the launch-count-dependent ordering is still correct. - testSearch("alphabetic", "AlphabeticD", "AlphabeticB", "AlphabeticA", "AlphabeticC"); - } - private void testSearch(String searchQuery, String... expectedResultsInOrder) { Cursor cursor = Applications.search(getMockContentResolver(), searchQuery); @@ -215,14 +205,6 @@ public class ApplicationsProviderTest extends ProviderTestCase2<ApplicationsProv } } - /** - * Makes the ApplicationsProvider increase the launch count of this - * application stored in its database. - */ - private void increaseLaunchCount(ComponentName componentName) { - Applications.increaseLaunchCount(getMockContentResolver(), componentName); - } - private ApplicationsProviderForTesting createNewProvider(Context context) throws Exception { ApplicationsProviderForTesting newProviderInstance = ApplicationsProviderForTesting.class.newInstance(); diff --git a/tests/src/com/android/providers/applications/MockActivityManager.java b/tests/src/com/android/providers/applications/MockActivityManager.java new file mode 100644 index 0000000..63deacf --- /dev/null +++ b/tests/src/com/android/providers/applications/MockActivityManager.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 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.providers.applications; + +import java.util.HashMap; +import java.util.Map; + +/** + * The real ActivityManager is difficult to mock out (has a package visibility + * constructor), so this doesn't extend it. + */ +public class MockActivityManager { + + private Map<String, Integer> mPackageLaunchCounts = new HashMap<String, Integer>(); + + public void addLaunchCount(String packageName, int launchCount) { + mPackageLaunchCounts.put(packageName, launchCount); + } + + public Map<String, Integer> getAllPackageLaunchCounts() { + return mPackageLaunchCounts; + } +} |