diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java index 04b2413b02c..eae49590647 100644 --- a/src/com/android/settings/SecuritySettings.java +++ b/src/com/android/settings/SecuritySettings.java @@ -77,6 +77,8 @@ import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.TileUtils; import java.util.ArrayList; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; import java.util.List; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; @@ -1032,8 +1034,8 @@ public class SecuritySettings extends SettingsPreferenceFragment MY_USER_ID)); } if (mPowerButtonInstantlyLocks != null) { - mPowerButtonInstantlyLocks.setChecked(mLockPatternUtils.getPowerButtonInstantlyLocks( - MY_USER_ID)); + mPowerButtonInstantlyLocks.setChecked( + mLockPatternUtils.getPowerButtonInstantlyLocks(MY_USER_ID)); } updateOwnerInfo(); @@ -1297,9 +1299,18 @@ public class SecuritySettings extends SettingsPreferenceFragment FeatureFactory.getFactory(mContext).getDashboardFeatureProvider(mContext); if (dashboardFeatureProvider.isEnabled() && (packageVerifierState == PACKAGE_VERIFIER_STATE_ENABLED)) { - DashboardCategory dashboardCategory = - dashboardFeatureProvider.getTilesForCategory(CategoryKey.CATEGORY_SECURITY); - mSummaryLoader.setSummary(this, getPackageVerifierSummary(dashboardCategory)); + // Calling the feature provider could potentially be slow, so do this on a separate + // thread so as to not block the loading of Settings. + Executors.newSingleThreadExecutor().execute(new Runnable() { + @Override + public void run() { + DashboardCategory dashboardCategory = + dashboardFeatureProvider.getTilesForCategory( + CategoryKey.CATEGORY_SECURITY); + mSummaryLoader.setSummary(SummaryProvider.this, + getPackageVerifierSummary(dashboardCategory)); + } + }); } else { final FingerprintManager fpm = Utils.getFingerprintManagerOrNull(mContext); if (fpm != null && fpm.isHardwareDetected()) { diff --git a/src/com/android/settings/security/SecurityFeatureProviderImpl.java b/src/com/android/settings/security/SecurityFeatureProviderImpl.java index 72d0f81c8d4..2d782a66852 100644 --- a/src/com/android/settings/security/SecurityFeatureProviderImpl.java +++ b/src/com/android/settings/security/SecurityFeatureProviderImpl.java @@ -22,9 +22,12 @@ import android.content.IContentProvider; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; import com.android.settings.trustagent.TrustAgentManager; import com.android.settings.trustagent.TrustAgentManagerImpl; import com.android.settingslib.drawer.DashboardCategory; +import android.support.annotation.VisibleForTesting; import android.support.v4.content.ContextCompat; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; @@ -35,6 +38,7 @@ import android.util.Pair; import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.TileUtils; +import java.util.concurrent.Executors; import java.util.Map; /** Implementation for {@code SecurityFeatureProvider}. */ @@ -43,8 +47,22 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider { private TrustAgentManager mTrustAgentManager; /** Update preferences with data from associated tiles. */ - public void updatePreferences(Context context, PreferenceScreen preferenceScreen, + public void updatePreferences(final Context context, final PreferenceScreen preferenceScreen, + final DashboardCategory dashboardCategory) { + // Fetching the summary and icon from the provider introduces latency, so do this on a + // separate thread. + Executors.newSingleThreadExecutor().execute(new Runnable() { + @Override + public void run() { + updatePreferencesToRunOnWorkerThread(context, preferenceScreen, dashboardCategory); + } + }); + } + + @VisibleForTesting + void updatePreferencesToRunOnWorkerThread(Context context, PreferenceScreen preferenceScreen, DashboardCategory dashboardCategory) { + if (preferenceScreen == null) { return; } @@ -82,26 +100,41 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider { TileUtils.getIconFromUri(context, packageName, iconUri, providerMap); if (icon != null) { // Icon is only returned if the icon belongs to Settings or the target app. - try { - matchingPref.setIcon(context.getPackageManager() - .getResourcesForApplication(icon.first /* package name */) - .getDrawable(icon.second /* res id */, context.getTheme())); - } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { - // Intentionally ignored. If icon resources cannot be found, do not update. - } + // setIcon must be called on the UI thread. + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + try { + matchingPref.setIcon(context.getPackageManager() + .getResourcesForApplication(icon.first /* package name */) + .getDrawable(icon.second /* res id */, + context.getTheme())); + } catch (PackageManager.NameNotFoundException + | Resources.NotFoundException e) { + // Intentionally ignored. If icon resources cannot be found, do not + // update. + } + } + }); } } if (!TextUtils.isEmpty(summaryUri)) { String summary = TileUtils.getTextFromUri(context, summaryUri, providerMap, TileUtils.META_DATA_PREFERENCE_SUMMARY); - // Only update the summary if it has actually changed. - if (summary == null) { - if (matchingPref.getSummary() != null) { - matchingPref.setSummary(summary); + // setSummary must be called on UI thread. + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + // Only update the summary if it has actually changed. + if (summary == null) { + if (matchingPref.getSummary() != null) { + matchingPref.setSummary(summary); + } + } else if (!summary.equals(matchingPref.getSummary())) { + matchingPref.setSummary(summary); + } } - } else if (!summary.equals(matchingPref.getSummary())) { - matchingPref.setSummary(summary); - } + }); } } } diff --git a/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java index 293c0aef709..49eb48b33e1 100644 --- a/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java @@ -42,6 +42,7 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowLooper; import java.util.Map; @@ -102,8 +103,11 @@ public class SecurityFeatureProviderImplTest { @Test public void updateTilesData_shouldNotProcessEmptyScreenOrTiles() { - mImpl.updatePreferences(mContext, null, null); - mImpl.updatePreferences(mContext, new PreferenceScreen(mContext, null), null); + mImpl.updatePreferencesToRunOnWorkerThread(mContext, null, null); + ShadowLooper.runUiThreadTasks(); + mImpl.updatePreferencesToRunOnWorkerThread( + mContext, new PreferenceScreen(mContext, null), null); + ShadowLooper.runUiThreadTasks(); verifyNoMoreInteractions(mPackageManager); } @@ -111,13 +115,17 @@ public class SecurityFeatureProviderImplTest { public void updateTilesData_shouldNotProcessNonMatchingPreference() { DashboardCategory dashboardCategory = new DashboardCategory(); dashboardCategory.addTile(new Tile()); - mImpl.updatePreferences(mContext, getPreferenceScreen(), dashboardCategory); + mImpl.updatePreferencesToRunOnWorkerThread( + mContext, getPreferenceScreen(), dashboardCategory); + ShadowLooper.runUiThreadTasks(); verifyNoMoreInteractions(mPackageManager); } @Test public void updateTilesData_shouldNotProcessMatchingPreferenceWithNoData() { - mImpl.updatePreferences(mContext, getPreferenceScreen(), getDashboardCategory()); + mImpl.updatePreferencesToRunOnWorkerThread( + mContext, getPreferenceScreen(), getDashboardCategory()); + ShadowLooper.runUiThreadTasks(); verifyNoMoreInteractions(mPackageManager); } @@ -135,7 +143,8 @@ public class SecurityFeatureProviderImplTest { dashboardCategory.getTile(0).intent = new Intent().setPackage("package"); dashboardCategory.getTile(0).metaData = bundle; - mImpl.updatePreferences(mContext, screen, dashboardCategory); + mImpl.updatePreferencesToRunOnWorkerThread(mContext, screen, dashboardCategory); + ShadowLooper.runUiThreadTasks(); verify(screen.findPreference(MOCK_KEY)).setIcon(mMockDrawable); verify(screen.findPreference(MOCK_KEY)).setSummary(MOCK_SUMMARY); } @@ -157,7 +166,8 @@ public class SecurityFeatureProviderImplTest { dashboardCategory.getTile(0).intent = new Intent().setPackage("package"); dashboardCategory.getTile(0).metaData = bundle; - mImpl.updatePreferences(mContext, screen, dashboardCategory); + mImpl.updatePreferencesToRunOnWorkerThread(mContext, screen, dashboardCategory); + ShadowLooper.runUiThreadTasks(); verify(screen.findPreference(MOCK_KEY), never()).setSummary(anyString()); }