diff --git a/res/values/strings.xml b/res/values/strings.xml index 6241850e4ec..fcc0b6d36bf 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9066,4 +9066,11 @@ This feature is not available on this device + + + "IMS registration state" + + "Registered" + + "Not registered" diff --git a/res/xml/device_info_status.xml b/res/xml/device_info_status.xml index 9a57af987bf..6fe43ec87b0 100644 --- a/res/xml/device_info_status.xml +++ b/res/xml/device_info_status.xml @@ -89,4 +89,10 @@ android:title="@string/status_wimax_mac_address" android:summary="@string/device_info_not_available" android:persistent="false" /> + diff --git a/src/com/android/settings/TrustedCredentialsSettings.java b/src/com/android/settings/TrustedCredentialsSettings.java index 4696dd317b3..587e814e2f8 100644 --- a/src/com/android/settings/TrustedCredentialsSettings.java +++ b/src/com/android/settings/TrustedCredentialsSettings.java @@ -61,6 +61,7 @@ import android.widget.Switch; import android.widget.TabHost; import android.widget.TextView; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.UnlaunchableAppActivity; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; @@ -152,6 +153,7 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment private int mConfirmingCredentialUser; private IntConsumer mConfirmingCredentialListener; private Set mAliasLoaders = new ArraySet(2); + @GuardedBy("mKeyChainConnectionByProfileId") private final SparseArray mKeyChainConnectionByProfileId = new SparseArray(); @@ -256,11 +258,13 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment } private void closeKeyChainConnections() { - final int n = mKeyChainConnectionByProfileId.size(); - for (int i = 0; i < n; ++i) { - mKeyChainConnectionByProfileId.valueAt(i).close(); + synchronized (mKeyChainConnectionByProfileId) { + final int n = mKeyChainConnectionByProfileId.size(); + for (int i = 0; i < n; ++i) { + mKeyChainConnectionByProfileId.valueAt(i).close(); + } + mKeyChainConnectionByProfileId.clear(); } - mKeyChainConnectionByProfileId.clear(); } private void addTab(Tab tab) { @@ -684,62 +688,64 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment SparseArray> certHoldersByProfile = new SparseArray>(); try { - List profiles = mUserManager.getUserProfiles(); - final int n = profiles.size(); - // First we get all aliases for all profiles in order to show progress - // correctly. Otherwise this could all be in a single loop. - SparseArray> aliasesByProfileId = new SparseArray< - List>(n); - int max = 0; - int progress = 0; - for (int i = 0; i < n; ++i) { - UserHandle profile = profiles.get(i); - int profileId = profile.getIdentifier(); - if (shouldSkipProfile(profile)) { - continue; + synchronized(mKeyChainConnectionByProfileId) { + List profiles = mUserManager.getUserProfiles(); + final int n = profiles.size(); + // First we get all aliases for all profiles in order to show progress + // correctly. Otherwise this could all be in a single loop. + SparseArray> aliasesByProfileId = new SparseArray< + List>(n); + int max = 0; + int progress = 0; + for (int i = 0; i < n; ++i) { + UserHandle profile = profiles.get(i); + int profileId = profile.getIdentifier(); + if (shouldSkipProfile(profile)) { + continue; + } + KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, + profile); + // Saving the connection for later use on the certificate dialog. + mKeyChainConnectionByProfileId.put(profileId, keyChainConnection); + IKeyChainService service = keyChainConnection.getService(); + List aliases = mTab.getAliases(service); + if (isCancelled()) { + return new SparseArray>(); + } + max += aliases.size(); + aliasesByProfileId.put(profileId, aliases); } - KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, - profile); - // Saving the connection for later use on the certificate dialog. - mKeyChainConnectionByProfileId.put(profileId, keyChainConnection); - IKeyChainService service = keyChainConnection.getService(); - List aliases = mTab.getAliases(service); - if (isCancelled()) { - return new SparseArray>(); + for (int i = 0; i < n; ++i) { + UserHandle profile = profiles.get(i); + int profileId = profile.getIdentifier(); + List aliases = aliasesByProfileId.get(profileId); + if (isCancelled()) { + return new SparseArray>(); + } + KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get( + profileId); + if (shouldSkipProfile(profile) || aliases == null + || keyChainConnection == null) { + certHoldersByProfile.put(profileId, new ArrayList(0)); + continue; + } + IKeyChainService service = keyChainConnection.getService(); + List certHolders = new ArrayList(max); + final int aliasMax = aliases.size(); + for (int j = 0; j < aliasMax; ++j) { + String alias = aliases.get(j); + byte[] encodedCertificate = service.getEncodedCaCertificate(alias, + true); + X509Certificate cert = KeyChain.toCertificate(encodedCertificate); + certHolders.add(new CertHolder(service, mAdapter, + mTab, alias, cert, profileId)); + publishProgress(++progress, max); + } + Collections.sort(certHolders); + certHoldersByProfile.put(profileId, certHolders); } - max += aliases.size(); - aliasesByProfileId.put(profileId, aliases); + return certHoldersByProfile; } - for (int i = 0; i < n; ++i) { - UserHandle profile = profiles.get(i); - int profileId = profile.getIdentifier(); - List aliases = aliasesByProfileId.get(profileId); - if (isCancelled()) { - return new SparseArray>(); - } - KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get( - profileId); - if (shouldSkipProfile(profile) || aliases == null - || keyChainConnection == null) { - certHoldersByProfile.put(profileId, new ArrayList(0)); - continue; - } - IKeyChainService service = keyChainConnection.getService(); - List certHolders = new ArrayList(max); - final int aliasMax = aliases.size(); - for (int j = 0; j < aliasMax; ++j) { - String alias = aliases.get(j); - byte[] encodedCertificate = service.getEncodedCaCertificate(alias, - true); - X509Certificate cert = KeyChain.toCertificate(encodedCertificate); - certHolders.add(new CertHolder(service, mAdapter, - mTab, alias, cert, profileId)); - publishProgress(++progress, max); - } - Collections.sort(certHolders); - certHoldersByProfile.put(profileId, certHolders); - } - return certHoldersByProfile; } catch (RemoteException e) { Log.e(TAG, "Remote exception while loading aliases.", e); return new SparseArray>(); @@ -936,16 +942,18 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment public List getX509CertsFromCertHolder(CertHolder certHolder) { List certificates = null; try { - KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get( - certHolder.mProfileId); - IKeyChainService service = keyChainConnection.getService(); - List chain = service.getCaCertificateChainAliases(certHolder.mAlias, true); - final int n = chain.size(); - certificates = new ArrayList(n); - for (int i = 0; i < n; ++i) { - byte[] encodedCertificate = service.getEncodedCaCertificate(chain.get(i), true); - X509Certificate certificate = KeyChain.toCertificate(encodedCertificate); - certificates.add(certificate); + synchronized (mKeyChainConnectionByProfileId) { + KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get( + certHolder.mProfileId); + IKeyChainService service = keyChainConnection.getService(); + List chain = service.getCaCertificateChainAliases(certHolder.mAlias, true); + final int n = chain.size(); + certificates = new ArrayList(n); + for (int i = 0; i < n; ++i) { + byte[] encodedCertificate = service.getEncodedCaCertificate(chain.get(i), true); + X509Certificate certificate = KeyChain.toCertificate(encodedCertificate); + certificates.add(certificate); + } } } catch (RemoteException ex) { Log.e(TAG, "RemoteException while retrieving certificate chain for root " @@ -985,15 +993,17 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment @Override protected Boolean doInBackground(Void... params) { try { - KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get( - mCertHolder.mProfileId); - IKeyChainService service = keyChainConnection.getService(); - if (mCertHolder.mDeleted) { - byte[] bytes = mCertHolder.mX509Cert.getEncoded(); - service.installCaCertificate(bytes); - return true; - } else { - return service.deleteCaCertificate(mCertHolder.mAlias); + synchronized (mKeyChainConnectionByProfileId) { + KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get( + mCertHolder.mProfileId); + IKeyChainService service = keyChainConnection.getService(); + if (mCertHolder.mDeleted) { + byte[] bytes = mCertHolder.mX509Cert.getEncoded(); + service.installCaCertificate(bytes); + return true; + } else { + return service.deleteCaCertificate(mCertHolder.mAlias); + } } } catch (CertificateEncodingException | SecurityException | IllegalStateException | RemoteException e) { diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java index 084b50ebb96..94ba478c5b3 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java @@ -188,22 +188,27 @@ public final class BluetoothDevicePreference extends GearPreference implements } void onClicked() { + Context context = getContext(); int bondState = mCachedDevice.getBondState(); final MetricsFeatureProvider metricsFeatureProvider = - FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider(); + FeatureFactory.getFactory(context).getMetricsFeatureProvider(); if (mCachedDevice.isConnected()) { - metricsFeatureProvider.action(getContext(), + metricsFeatureProvider.action(context, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT); askDisconnect(); } else if (bondState == BluetoothDevice.BOND_BONDED) { - metricsFeatureProvider.action(getContext(), + metricsFeatureProvider.action(context, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT); mCachedDevice.connect(true); } else if (bondState == BluetoothDevice.BOND_NONE) { - metricsFeatureProvider.action(getContext(), + metricsFeatureProvider.action(context, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR); + if (!mCachedDevice.hasHumanReadableName()) { + metricsFeatureProvider.action(context, + MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES); + } pair(); } } diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java index c06a58fe24c..fff70023371 100644 --- a/src/com/android/settings/dashboard/DashboardAdapter.java +++ b/src/com/android/settings/dashboard/DashboardAdapter.java @@ -94,19 +94,6 @@ public class DashboardAdapter extends RecyclerView.Adapter conditions, SuggestionParser suggestionParser, SuggestionDismissController.Callback callback) { @@ -129,7 +116,7 @@ public class DashboardAdapter extends RecyclerView.Adapter 0 && data.conditionCount > 0 - && curMode != DashboardData.HEADER_MODE_SUGGESTION_EXPANDED - ? DashboardData.HEADER_MODE_SUGGESTION_EXPANDED - : DashboardData.HEADER_MODE_FULLY_EXPANDED; + && curMode != DashboardData.HEADER_MODE_SUGGESTION_EXPANDED + ? DashboardData.HEADER_MODE_SUGGESTION_EXPANDED + : DashboardData.HEADER_MODE_FULLY_EXPANDED; final boolean moreSuggestions = data.hiddenSuggestionCount > 0; final boolean hasConditions = data.conditionCount > 0; if (data.conditionCount > 0) { @@ -377,22 +364,22 @@ public class DashboardAdapter extends RecyclerView.Adapter 0) { holder.summary.setText(mContext.getResources().getQuantityString( - R.plurals.suggestions_collapsed_summary, - data.hiddenSuggestionCount, data.hiddenSuggestionCount)); + R.plurals.suggestions_collapsed_summary, + data.hiddenSuggestionCount, data.hiddenSuggestionCount)); } else { holder.title.setText(mContext.getResources().getQuantityString( - R.plurals.suggestions_collapsed_title, - data.hiddenSuggestionCount, data.hiddenSuggestionCount)); + R.plurals.suggestions_collapsed_title, + data.hiddenSuggestionCount, data.hiddenSuggestionCount)); holder.title.setTextColor(Color.BLACK); holder.summary.setText(null); } } else if (curMode == DashboardData.HEADER_MODE_DEFAULT) { if (data.conditionCount > 0) { holder.summary.setText(mContext.getString( - R.string.suggestions_summary, data.hiddenSuggestionCount)); + R.string.suggestions_summary, data.hiddenSuggestionCount)); } else { holder.title.setText(mContext.getString( - R.string.suggestions_more_title, data.hiddenSuggestionCount)); + R.string.suggestions_more_title, data.hiddenSuggestionCount)); holder.title.setTextColor(Color.BLACK); holder.summary.setText(null); } @@ -400,7 +387,7 @@ public class DashboardAdapter extends RecyclerView.Adapter 1) { holder.summary.setTextColor(Utils.getColorAccent(mContext)); holder.summary.setText( - mContext.getString(R.string.condition_summary, data.conditionCount)); + mContext.getString(R.string.condition_summary, data.conditionCount)); } else { holder.summary.setText(null); } @@ -413,16 +400,16 @@ public class DashboardAdapter extends RecyclerView.Adapter { - if (moreSuggestions ) { + if (moreSuggestions) { logSuggestions(); } else if (hasConditions) { mMetricsFeatureProvider.action(mContext, - MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true); + MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true); } DashboardData prevData = mDashboardData; final boolean wasCollapsed = curMode == DashboardData.HEADER_MODE_COLLAPSED; mDashboardData = new DashboardData.Builder(prevData) - .setSuggestionConditionMode(nextMode).build(); + .setSuggestionConditionMode(nextMode).build(); notifyDashboardDataChanged(prevData); if (wasCollapsed) { mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION); @@ -439,13 +426,13 @@ public class DashboardAdapter extends RecyclerView.Adapter 0) { mSuggestionAdapter = new SuggestionAdapter(mContext, (List) - mDashboardData.getItemEntityByPosition(position), mSuggestionsShownLogged); + mDashboardData.getItemEntityByPosition(position), mSuggestionsShownLogged); mSuggestionDismissHandler = new SuggestionDismissController(mContext, - holder.data, mSuggestionParser, mCallback); + holder.data, mSuggestionParser, mCallback); holder.data.setAdapter(mSuggestionAdapter); } else { ConditionAdapter adapter = new ConditionAdapter(mContext, - (List) mDashboardData.getItemEntityByPosition(position), + (List) mDashboardData.getItemEntityByPosition(position), mDashboardData.getSuggestionConditionMode()); adapter.addDismissHandling(holder.data); holder.data.setAdapter(adapter); @@ -454,19 +441,13 @@ public class DashboardAdapter extends RecyclerView.Adapter icons, ViewGroup parent) { @@ -519,7 +500,7 @@ public class DashboardAdapter extends RecyclerView.Adapter result = new ArrayList(); + List result = new ArrayList<>(); final int size = conditions == null ? 0 : conditions.size(); for (int i = 0; i < size; i++) { final Condition condition = conditions.get(i); @@ -460,6 +436,17 @@ public class DashboardData { // Only check title and summary for dashboard tile return TextUtils.equals(localTile.title, targetTile.title) && TextUtils.equals(localTile.summary, targetTile.summary); + case TYPE_SUGGESTION_CONDITION_CONTAINER: + // If entity is suggestion and contains remote view, force refresh + final List entities = (List) entity; + if (!entities.isEmpty()) { + Object firstEntity = entities.get(0); + if (firstEntity instanceof Tile + && ((Tile) firstEntity).remoteViews != null) { + return false; + } + } + // Otherwise Fall through to default default: return entity == null ? targetItem.entity == null : entity.equals(targetItem.entity); @@ -482,7 +469,7 @@ public class DashboardData { conditionCount = sizeOf(conditions); this.hiddenSuggestionCount = hiddenSuggestionCount; title = conditionCount > 0 ? conditions.get(0).getTitle() : null; - conditionIcons = new ArrayList(); + conditionIcons = new ArrayList<>(); for (int i = 0; conditions != null && i < conditions.size(); i++) { final Condition condition = conditions.get(i); conditionIcons.add(condition.getIcon()); diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index b920850d485..53a51cd96ed 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -230,6 +230,9 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment @VisibleForTesting boolean tintTileIcon(Tile tile) { + if (tile.icon == null) { + return false; + } // First check if the tile has set the icon tintable metadata. final Bundle metadata = tile.metaData; if (metadata != null diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java index 45396807f87..2c9da4124b1 100644 --- a/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java +++ b/src/com/android/settings/dashboard/suggestions/SuggestionAdapter.java @@ -29,6 +29,8 @@ import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder; import com.android.settings.dashboard.DashboardAdapter.IconCache; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.drawer.Tile; +import com.android.settingslib.drawer.TileUtils; + import java.util.List; import java.util.Objects; @@ -73,6 +75,7 @@ public class SuggestionAdapter extends RecyclerView.Adapter mSuggestionsShownLogged.add(suggestionId); } if (suggestion.remoteViews != null) { + TileUtils.updateTileUsingSummaryUri(mContext, suggestion); final ViewGroup itemView = (ViewGroup) holder.itemView; itemView.removeAllViews(); itemView.addView(suggestion.remoteViews.apply(itemView.getContext(), itemView)); diff --git a/src/com/android/settings/deletionhelper/AutomaticStorageManagerDescriptionPreferenceController.java b/src/com/android/settings/deletionhelper/AutomaticStorageManagerDescriptionPreferenceController.java new file mode 100644 index 00000000000..261f66cea6e --- /dev/null +++ b/src/com/android/settings/deletionhelper/AutomaticStorageManagerDescriptionPreferenceController.java @@ -0,0 +1,76 @@ +/** + * Copyright (C) 2017 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.settings.deletionhelper; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.text.format.DateUtils; +import android.text.format.Formatter; + +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.core.AbstractPreferenceController; + +/** + * Handles the wall of text which appears below the options in the Storage Management settings drill + * down. + */ +public class AutomaticStorageManagerDescriptionPreferenceController + extends AbstractPreferenceController implements PreferenceControllerMixin { + private static final String KEY_FREED = "freed_bytes"; + + public AutomaticStorageManagerDescriptionPreferenceController(Context context) { + super(context); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_FREED; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + Preference preference = screen.findPreference(getPreferenceKey()); + final Context context = preference.getContext(); + ContentResolver cr = context.getContentResolver(); + long freedBytes = + Settings.Secure.getLong( + cr, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED, 0); + long lastRunMillis = + Settings.Secure.getLong(cr, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN, 0); + if (freedBytes == 0 || lastRunMillis == 0 || !isStorageManagerEnabled(cr)) { + preference.setSummary(R.string.automatic_storage_manager_text); + } else { + preference.setSummary( + context.getString( + R.string.automatic_storage_manager_freed_bytes, + Formatter.formatFileSize(context, freedBytes), + DateUtils.formatDateTime( + context, lastRunMillis, DateUtils.FORMAT_SHOW_DATE))); + } + } + + private boolean isStorageManagerEnabled(ContentResolver cr) { + return Settings.Secure.getInt(cr, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0) + != 0; + } +} diff --git a/src/com/android/settings/deletionhelper/AutomaticStorageManagerSettings.java b/src/com/android/settings/deletionhelper/AutomaticStorageManagerSettings.java index f7b62b1666f..e38317a824d 100644 --- a/src/com/android/settings/deletionhelper/AutomaticStorageManagerSettings.java +++ b/src/com/android/settings/deletionhelper/AutomaticStorageManagerSettings.java @@ -16,15 +16,13 @@ package com.android.settings.deletionhelper; -import android.app.Activity; import android.content.ContentResolver; +import android.content.Context; import android.os.Bundle; import android.provider.Settings; import android.support.v7.preference.DropDownPreference; import android.support.v7.preference.Preference; import android.support.v7.preference.Preference.OnPreferenceChangeListener; -import android.text.format.DateUtils; -import android.text.format.Formatter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -32,39 +30,34 @@ import android.view.ViewGroup; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.SettingsActivity; -import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; import com.android.settings.widget.SwitchBar; +import com.android.settingslib.core.AbstractPreferenceController; + +import java.util.ArrayList; +import java.util.List; /** * AutomaticStorageManagerSettings is the Settings screen for configuration and management of the * automatic storage manager. */ -public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment +public class AutomaticStorageManagerSettings extends DashboardFragment implements OnPreferenceChangeListener { private static final String KEY_DAYS = "days"; - private static final String KEY_FREED = "freed_bytes"; - private static final String STORAGE_MANAGER_ENABLED_BY_DEFAULT_PROPERTY = - "ro.storage_manager.enabled"; private AutomaticStorageManagerSwitchBarController mSwitchController; private DropDownPreference mDaysToRetain; - private Preference mFreedBytes; private SwitchBar mSwitchBar; - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.automatic_storage_management_settings); - } - @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); initializeDaysToRetainPreference(); - initializeFreedBytesPreference(); initializeSwitchBar(); return view; @@ -98,35 +91,25 @@ public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment getFragmentManager()); } - private void initializeFreedBytesPreference() { - ContentResolver cr = getContentResolver(); - mFreedBytes = findPreference(KEY_FREED); - long freedBytes = Settings.Secure.getLong(cr, - Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED, - 0); - long lastRunMillis = Settings.Secure.getLong(cr, - Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN, - 0); - if (freedBytes == 0 || lastRunMillis == 0) { - mFreedBytes.setVisible(false); - } else { - final Activity activity = getActivity(); - mFreedBytes.setSummary( - activity.getString( - R.string.automatic_storage_manager_freed_bytes, - Formatter.formatFileSize(activity, freedBytes), - DateUtils.formatDateTime( - activity, lastRunMillis, DateUtils.FORMAT_SHOW_DATE))); - } - } - @Override public void onResume() { super.onResume(); - boolean isStorageManagerChecked = - Settings.Secure.getInt(getContentResolver(), - Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0) != 0; - mDaysToRetain.setEnabled(isStorageManagerChecked); + mDaysToRetain.setEnabled(isStorageManagerEnabled()); + } + + @Override + protected String getLogTag() { + return null; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.automatic_storage_management_settings; + } + + @Override + protected List getPreferenceControllers(Context context) { + return buildPreferenceControllers(context); } @Override @@ -168,4 +151,30 @@ public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment return indices.length - 1; } + private boolean isStorageManagerEnabled() { + return Settings.Secure.getInt( + getContentResolver(), Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0) + != 0; + } + + private static List buildPreferenceControllers(Context context) { + final List controllers = new ArrayList<>(); + controllers.add(new AutomaticStorageManagerDescriptionPreferenceController(context)); + return controllers; + } + + /** For Search. */ + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + protected boolean isPageSearchEnabled(Context context) { + return false; + } + + @Override + public List getPreferenceControllers( + Context context) { + return buildPreferenceControllers(context); + } + }; } diff --git a/src/com/android/settings/development/DevelopmentSettings.java b/src/com/android/settings/development/DevelopmentSettings.java index 3ee1295e802..3759ea1b9e2 100644 --- a/src/com/android/settings/development/DevelopmentSettings.java +++ b/src/com/android/settings/development/DevelopmentSettings.java @@ -53,6 +53,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.SystemProperties; +import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IStorageManager; import android.provider.SearchIndexableResource; @@ -1029,8 +1030,19 @@ public class DevelopmentSettings extends RestrictedSettingsFragment return context.getSystemService(Context.OEM_LOCK_SERVICE) != null; } + /** + * Returns whether OEM unlock is allowed by the user and carrier. + * + * This does not take into account any restrictions imposed by the device policy. + */ + private boolean isOemUnlockAllowedByUserAndCarrier() { + final UserHandle userHandle = UserHandle.of(UserHandle.myUserId()); + return mOemLockManager.isOemUnlockAllowedByCarrier() + && !mUm.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET, userHandle); + } + private boolean enableOemUnlockPreference() { - return !isBootloaderUnlocked() && mOemLockManager.canUserAllowOemUnlock(); + return !isBootloaderUnlocked() && isOemUnlockAllowedByUserAndCarrier(); } private void updateOemUnlockOptions() { @@ -1044,10 +1056,6 @@ public class DevelopmentSettings extends RestrictedSettingsFragment // Check restriction, disable mEnableOemUnlock and apply policy transparency. mEnableOemUnlock.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET); } - if (mEnableOemUnlock.isEnabled()) { - // Check restriction, disable mEnableOemUnlock and apply policy transparency. - mEnableOemUnlock.checkRestrictionAndSetDisabled(UserManager.DISALLOW_OEM_UNLOCK); - } } } @@ -2572,7 +2580,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_bootloader_unlocked; } else if (isSimLockedDevice()) { oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_sim_locked_device; - } else if (!mOemLockManager.canUserAllowOemUnlock()) { + } else if (!isOemUnlockAllowedByUserAndCarrier()) { // If the device isn't SIM-locked but OEM unlock is disallowed by some party, this // means either some other carrier restriction is in place or the device hasn't been // able to confirm which restrictions (SIM-lock or otherwise) apply. diff --git a/src/com/android/settings/deviceinfo/Status.java b/src/com/android/settings/deviceinfo/Status.java index baddc6cac2b..faa4134a935 100644 --- a/src/com/android/settings/deviceinfo/Status.java +++ b/src/com/android/settings/deviceinfo/Status.java @@ -28,11 +28,15 @@ import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.PersistableBundle; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserManager; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -56,6 +60,7 @@ public class Status extends SettingsPreferenceFragment { private static final String KEY_WIMAX_MAC_ADDRESS = "wimax_mac_address"; private static final String KEY_SIM_STATUS = "sim_status"; private static final String KEY_IMEI_INFO = "imei_info"; + private static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state"; // Broadcasts to listen to for connectivity changes. private static final String[] CONNECTIVITY_INTENTS = { @@ -85,6 +90,8 @@ public class Status extends SettingsPreferenceFragment { private Preference mIpAddress; private Preference mWifiMacAddress; private Preference mWimaxMacAddress; + private Preference mImsStatus; + private Handler mHandler; private static class MyHandler extends Handler { @@ -162,6 +169,7 @@ public class Status extends SettingsPreferenceFragment { mWifiMacAddress = findPreference(KEY_WIFI_MAC_ADDRESS); mWimaxMacAddress = findPreference(KEY_WIMAX_MAC_ADDRESS); mIpAddress = findPreference(KEY_IP_ADDRESS); + mImsStatus = findPreference(KEY_IMS_REGISTRATION_STATE); mRes = getResources(); mUnavailable = mRes.getString(R.string.status_unavailable); @@ -269,11 +277,31 @@ public class Status extends SettingsPreferenceFragment { } } + private void setImsRegistrationStatus() { + CarrierConfigManager configManager = (CarrierConfigManager) + getSystemService(Context.CARRIER_CONFIG_SERVICE); + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + PersistableBundle config = null; + if (configManager != null) { + config = configManager.getConfigForSubId(subId); + } + if (config != null && config.getBoolean( + CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL)) { + TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ? + R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered); + } else { + removePreferenceFromScreen(KEY_IMS_REGISTRATION_STATE); + mImsStatus = null; + } + } + void updateConnectivity() { setWimaxStatus(); setWifiStatus(); setBtStatus(); setIpAddressStatus(); + setImsRegistrationStatus(); } void updateTimes() { diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java index 33d7d36b85c..dd0db9a2335 100644 --- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java +++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java @@ -40,6 +40,7 @@ import com.android.settings.applications.UserManagerWrapper; import com.android.settings.applications.UserManagerWrapperImpl; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController; +import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper; import com.android.settings.deviceinfo.storage.SecondaryUserController; import com.android.settings.deviceinfo.storage.StorageAsyncLoader; import com.android.settings.deviceinfo.storage.StorageItemPreferenceController; @@ -68,6 +69,7 @@ public class StorageDashboardFragment extends DashboardFragment private VolumeInfo mVolume; private PrivateStorageInfo mStorageInfo; private SparseArray mAppsResult; + private CachedStorageValuesHelper mCachedStorageValuesHelper; private StorageSummaryDonutPreferenceController mSummaryController; private StorageItemPreferenceController mPreferenceController; @@ -102,7 +104,10 @@ public class StorageDashboardFragment extends DashboardFragment @Override public void onViewCreated(View v, Bundle savedInstanceState) { super.onViewCreated(v, savedInstanceState); - setLoading(true, false); + initializeCacheProvider(); + if (mAppsResult == null || mStorageInfo == null) { + setLoading(true, false); + } } @Override @@ -249,6 +254,7 @@ public class StorageDashboardFragment extends DashboardFragment public void onLoadFinished(Loader> loader, SparseArray data) { mAppsResult = data; + maybeCacheFreshValues(); onReceivedSizes(); } @@ -256,6 +262,48 @@ public class StorageDashboardFragment extends DashboardFragment public void onLoaderReset(Loader> loader) { } + @VisibleForTesting + public void setCachedStorageValuesHelper(CachedStorageValuesHelper helper) { + mCachedStorageValuesHelper = helper; + } + + @VisibleForTesting + public PrivateStorageInfo getPrivateStorageInfo() { + return mStorageInfo; + } + + @VisibleForTesting + public SparseArray getAppsStorageResult() { + return mAppsResult; + } + + @VisibleForTesting + public void initializeCachedValues() { + PrivateStorageInfo info = mCachedStorageValuesHelper.getCachedPrivateStorageInfo(); + SparseArray loaderResult = + mCachedStorageValuesHelper.getCachedAppsStorageResult(); + if (info == null || loaderResult == null) { + return; + } + + mStorageInfo = info; + mAppsResult = loaderResult; + } + + private void initializeCacheProvider() { + mCachedStorageValuesHelper = + new CachedStorageValuesHelper(getContext(), UserHandle.myUserId()); + initializeCachedValues(); + onReceivedSizes(); + } + + private void maybeCacheFreshValues() { + if (mStorageInfo != null && mAppsResult != null) { + mCachedStorageValuesHelper.cacheResult( + mStorageInfo, mAppsResult.get(UserHandle.myUserId())); + } + } + /** * IconLoaderCallbacks exists because StorageDashboardFragment already implements * LoaderCallbacks for a different type. @@ -308,6 +356,7 @@ public class StorageDashboardFragment extends DashboardFragment } mStorageInfo = privateStorageInfo; + maybeCacheFreshValues(); onReceivedSizes(); } } diff --git a/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelper.java b/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelper.java new file mode 100644 index 00000000000..8225db39434 --- /dev/null +++ b/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelper.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2017 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.settings.deviceinfo.storage; + +import android.content.Context; +import android.content.SharedPreferences; +import android.provider.Settings; +import android.support.annotation.VisibleForTesting; +import android.util.SparseArray; + +import com.android.settingslib.applications.StorageStatsSource; +import com.android.settingslib.deviceinfo.PrivateStorageInfo; + +import java.util.concurrent.TimeUnit; + +public class CachedStorageValuesHelper { + + @VisibleForTesting public static final String SHARED_PREFERENCES_NAME = "CachedStorageValues"; + public static final String TIMESTAMP_KEY = "last_query_timestamp"; + public static final String FREE_BYTES_KEY = "free_bytes"; + public static final String TOTAL_BYTES_KEY = "total_bytes"; + public static final String GAME_APPS_SIZE_KEY = "game_apps_size"; + public static final String MUSIC_APPS_SIZE_KEY = "music_apps_size"; + public static final String VIDEO_APPS_SIZE_KEY = "video_apps_size"; + public static final String PHOTO_APPS_SIZE_KEY = "photo_apps_size"; + public static final String OTHER_APPS_SIZE_KEY = "other_apps_size"; + public static final String CACHE_APPS_SIZE_KEY = "cache_apps_size"; + public static final String EXTERNAL_TOTAL_BYTES = "external_total_bytes"; + public static final String EXTERNAL_AUDIO_BYTES = "external_audio_bytes"; + public static final String EXTERNAL_VIDEO_BYTES = "external_video_bytes"; + public static final String EXTERNAL_IMAGE_BYTES = "external_image_bytes"; + public static final String EXTERNAL_APP_BYTES = "external_apps_bytes"; + public static final String USER_ID_KEY = "user_id"; + private final Long mClobberThreshold; + private final SharedPreferences mSharedPreferences; + private final int mUserId; + // This clock is used to provide the time. By default, it uses the system clock, but can be + // replaced for test purposes. + protected Clock mClock; + + public CachedStorageValuesHelper(Context context, int userId) { + mSharedPreferences = + context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); + mClock = new Clock(); + mUserId = userId; + mClobberThreshold = + Settings.Global.getLong( + context.getContentResolver(), + Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD, + TimeUnit.MINUTES.toMillis(5)); + } + + public PrivateStorageInfo getCachedPrivateStorageInfo() { + if (!isDataValid()) { + return null; + } + final long freeBytes = mSharedPreferences.getLong(FREE_BYTES_KEY, -1); + final long totalBytes = mSharedPreferences.getLong(TOTAL_BYTES_KEY, -1); + if (freeBytes < 0 || totalBytes < 0) { + return null; + } + + return new PrivateStorageInfo(freeBytes, totalBytes); + } + + public SparseArray getCachedAppsStorageResult() { + if (!isDataValid()) { + return null; + } + final long gamesSize = mSharedPreferences.getLong(GAME_APPS_SIZE_KEY, -1); + final long musicAppsSize = mSharedPreferences.getLong(MUSIC_APPS_SIZE_KEY, -1); + final long videoAppsSize = mSharedPreferences.getLong(VIDEO_APPS_SIZE_KEY, -1); + final long photoAppSize = mSharedPreferences.getLong(PHOTO_APPS_SIZE_KEY, -1); + final long otherAppsSize = mSharedPreferences.getLong(OTHER_APPS_SIZE_KEY, -1); + final long cacheSize = mSharedPreferences.getLong(CACHE_APPS_SIZE_KEY, -1); + if (gamesSize < 0 + || musicAppsSize < 0 + || videoAppsSize < 0 + || photoAppSize < 0 + || otherAppsSize < 0 + || cacheSize < 0) { + return null; + } + + final long externalTotalBytes = mSharedPreferences.getLong(EXTERNAL_TOTAL_BYTES, -1); + final long externalAudioBytes = mSharedPreferences.getLong(EXTERNAL_AUDIO_BYTES, -1); + final long externalVideoBytes = mSharedPreferences.getLong(EXTERNAL_VIDEO_BYTES, -1); + final long externalImageBytes = mSharedPreferences.getLong(EXTERNAL_IMAGE_BYTES, -1); + final long externalAppBytes = mSharedPreferences.getLong(EXTERNAL_APP_BYTES, -1); + if (externalTotalBytes < 0 + || externalAudioBytes < 0 + || externalVideoBytes < 0 + || externalImageBytes < 0 + || externalAppBytes < 0) { + return null; + } + + final StorageStatsSource.ExternalStorageStats externalStats = + new StorageStatsSource.ExternalStorageStats( + externalTotalBytes, + externalAudioBytes, + externalVideoBytes, + externalImageBytes, + externalAppBytes); + final StorageAsyncLoader.AppsStorageResult result = + new StorageAsyncLoader.AppsStorageResult(); + result.gamesSize = gamesSize; + result.musicAppsSize = musicAppsSize; + result.videoAppsSize = videoAppsSize; + result.photosAppsSize = photoAppSize; + result.otherAppsSize = otherAppsSize; + result.cacheSize = cacheSize; + result.externalStats = externalStats; + final SparseArray resultArray = new SparseArray<>(); + resultArray.append(mUserId, result); + return resultArray; + } + + public void cacheResult( + PrivateStorageInfo storageInfo, StorageAsyncLoader.AppsStorageResult result) { + mSharedPreferences + .edit() + .putLong(FREE_BYTES_KEY, storageInfo.freeBytes) + .putLong(TOTAL_BYTES_KEY, storageInfo.totalBytes) + .putLong(GAME_APPS_SIZE_KEY, result.gamesSize) + .putLong(MUSIC_APPS_SIZE_KEY, result.musicAppsSize) + .putLong(VIDEO_APPS_SIZE_KEY, result.videoAppsSize) + .putLong(PHOTO_APPS_SIZE_KEY, result.photosAppsSize) + .putLong(OTHER_APPS_SIZE_KEY, result.otherAppsSize) + .putLong(CACHE_APPS_SIZE_KEY, result.cacheSize) + .putLong(EXTERNAL_TOTAL_BYTES, result.externalStats.totalBytes) + .putLong(EXTERNAL_AUDIO_BYTES, result.externalStats.audioBytes) + .putLong(EXTERNAL_VIDEO_BYTES, result.externalStats.videoBytes) + .putLong(EXTERNAL_IMAGE_BYTES, result.externalStats.imageBytes) + .putLong(EXTERNAL_APP_BYTES, result.externalStats.appBytes) + .putInt(USER_ID_KEY, mUserId) + .putLong(TIMESTAMP_KEY, mClock.getCurrentTime()) + .apply(); + } + + private boolean isDataValid() { + final int cachedUserId = mSharedPreferences.getInt(USER_ID_KEY, -1); + if (cachedUserId != mUserId) { + return false; + } + + final long lastQueryTime = mSharedPreferences.getLong(TIMESTAMP_KEY, Long.MAX_VALUE); + final long currentTime = mClock.getCurrentTime(); + return currentTime - lastQueryTime < mClobberThreshold; + } + + /** Clock provides the current time. */ + static class Clock { + public long getCurrentTime() { + return System.currentTimeMillis(); + } + } +} diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java index ca85f69d0be..163f5b9dff4 100644 --- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java +++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java @@ -208,23 +208,18 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle public void setUserId(UserHandle userHandle) { mUserId = userHandle.getIdentifier(); - PackageManager pm = mContext.getPackageManager(); - badgePreference(pm, userHandle, mPhotoPreference); - badgePreference(pm, userHandle, mMoviesPreference); - badgePreference(pm, userHandle, mAudioPreference); - badgePreference(pm, userHandle, mGamePreference); - badgePreference(pm, userHandle, mAppPreference); - badgePreference(pm, userHandle, mSystemPreference); - badgePreference(pm, userHandle, mFilePreference); + tintPreference(mPhotoPreference); + tintPreference(mMoviesPreference); + tintPreference(mAudioPreference); + tintPreference(mGamePreference); + tintPreference(mAppPreference); + tintPreference(mSystemPreference); + tintPreference(mFilePreference); } - private void badgePreference(PackageManager pm, UserHandle userHandle, Preference preference) { + private void tintPreference(Preference preference) { if (preference != null) { - Drawable currentIcon = preference.getIcon(); - // Sigh... Applying the badge to the icon clobbers the tint on the base drawable. - // For some reason, reapplying it here means the tint remains. - currentIcon = applyTint(mContext, currentIcon); - preference.setIcon(pm.getUserBadgedIcon(currentIcon, userHandle)); + preference.setIcon(applyTint(mContext, preference.getIcon())); } } diff --git a/src/com/android/settings/search/DatabaseIndexingManager.java b/src/com/android/settings/search/DatabaseIndexingManager.java index 7f6f0129548..a0f47d16542 100644 --- a/src/com/android/settings/search/DatabaseIndexingManager.java +++ b/src/com/android/settings/search/DatabaseIndexingManager.java @@ -17,27 +17,6 @@ package com.android.settings.search; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_RANK; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE; -import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID; import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ID; import static com.android.settings.search.DatabaseResultLoader .COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE; @@ -70,17 +49,14 @@ import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.USER_ import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.XmlResourceParser; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; -import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.provider.SearchIndexableData; @@ -89,7 +65,6 @@ import android.provider.SearchIndexablesContract; import android.support.annotation.DrawableRes; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; -import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; @@ -98,13 +73,13 @@ import com.android.settings.SettingsActivity; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.overlay.FeatureFactory; +import com.android.settings.search.indexing.IndexableDataCollector; +import com.android.settings.search.indexing.PreIndexData; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -132,23 +107,14 @@ public class DatabaseIndexingManager { private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference"; private static final String NODE_NAME_LIST_PREFERENCE = "ListPreference"; - private static final List EMPTY_LIST = Collections.emptyList(); - - private final String mBaseAuthority; - @VisibleForTesting final AtomicBoolean mIsIndexingComplete = new AtomicBoolean(false); - @VisibleForTesting - final UpdateData mDataToProcess = new UpdateData(); + private IndexableDataCollector mCollector; + private Context mContext; - public DatabaseIndexingManager(Context context, String baseAuthority) { - mContext = context; - mBaseAuthority = baseAuthority; - } - - public void setContext(Context context) { + public DatabaseIndexingManager(Context context) { mContext = context; } @@ -177,33 +143,17 @@ public class DatabaseIndexingManager { final String providerVersionedNames = IndexDatabaseHelper.buildProviderVersionedNames(providers); - final boolean isFullIndex = IndexDatabaseHelper.isFullIndex(mContext, localeStr, + final boolean isFullIndex = isFullIndex(mContext, localeStr, fingerprint, providerVersionedNames); if (isFullIndex) { rebuildDatabase(); } - for (final ResolveInfo info : providers) { - if (!DatabaseIndexingUtils.isWellKnownProvider(info, mContext)) { - continue; - } - final String authority = info.providerInfo.authority; - final String packageName = info.providerInfo.packageName; + PreIndexData indexData = getIndexDataFromProviders(providers, isFullIndex); - if (isFullIndex) { - addIndexablesFromRemoteProvider(packageName, authority); - } - final long nonIndexableStartTime = System.currentTimeMillis(); - addNonIndexablesKeysFromRemoteProvider(packageName, authority); - if (SettingsSearchIndexablesProvider.DEBUG) { - final long nonIndextableTime = System.currentTimeMillis() - nonIndexableStartTime; - Log.d(LOG_TAG, "performIndexing update non-indexable for package " + packageName - + " took time: " + nonIndextableTime); - } - } final long updateDatabaseStartTime = System.currentTimeMillis(); - updateDatabase(isFullIndex, localeStr); + updateDatabase(indexData, isFullIndex, localeStr); if (SettingsSearchIndexablesProvider.DEBUG) { final long updateDatabaseTime = System.currentTimeMillis() - updateDatabaseStartTime; Log.d(LOG_TAG, "performIndexing updateDatabase took time: " + updateDatabaseTime); @@ -221,10 +171,37 @@ public class DatabaseIndexingManager { } } + @VisibleForTesting + PreIndexData getIndexDataFromProviders(List providers, boolean isFullIndex) { + if (mCollector == null) { + mCollector = new IndexableDataCollector(mContext); + } + return mCollector.collectIndexableData(providers, isFullIndex); + } + /** - * Reconstruct the database in the following cases: - * - Language has changed - * - Build has changed + * Checks if the indexed data is obsolete, when either: + * - Device language has changed + * - Device has taken an OTA. + * In both cases, the device requires a full index. + * + * @param locale is the default for the device + * @param fingerprint id for the current build. + * @return true if a full index should be preformed. + */ + @VisibleForTesting + boolean isFullIndex(Context context, String locale, String fingerprint, + String providerVersionedNames) { + final boolean isLocaleIndexed = IndexDatabaseHelper.isLocaleAlreadyIndexed(context, locale); + final boolean isBuildIndexed = IndexDatabaseHelper.isBuildIndexed(context, fingerprint); + final boolean areProvidersIndexed = IndexDatabaseHelper + .areProvidersIndexed(context, providerVersionedNames); + + return !(isLocaleIndexed && isBuildIndexed && areProvidersIndexed); + } + + /** + * Drop the currently stored database, and clear the flags which mark the database as indexed. */ private void rebuildDatabase() { // Drop the database when the locale or build has changed. This eliminates rows which are @@ -244,16 +221,9 @@ public class DatabaseIndexingManager { * @param localeStr the default locale for the device. */ @VisibleForTesting - void updateDatabase(boolean needsReindexing, String localeStr) { - final UpdateData copy; - - synchronized (mDataToProcess) { - copy = mDataToProcess.copy(); - mDataToProcess.clear(); - } - - final List dataToUpdate = copy.dataToUpdate; - final Map> nonIndexableKeys = copy.nonIndexableKeys; + void updateDatabase(PreIndexData indexData, boolean needsReindexing, String localeStr) { + final List dataToUpdate = indexData.dataToUpdate; + final Map> nonIndexableKeys = indexData.nonIndexableKeys; final SQLiteDatabase database = getWritableDatabase(); if (database == null) { @@ -378,99 +348,10 @@ public class DatabaseIndexingManager { disabledResults.close(); } - @VisibleForTesting - boolean addIndexablesFromRemoteProvider(String packageName, String authority) { - try { - final Context context = mBaseAuthority.equals(authority) ? - mContext : mContext.createPackageContext(packageName, 0); - - final Uri uriForResources = buildUriForXmlResources(authority); - addIndexablesForXmlResourceUri(context, packageName, uriForResources, - SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS); - - final Uri uriForRawData = buildUriForRawData(authority); - addIndexablesForRawDataUri(context, packageName, uriForRawData, - SearchIndexablesContract.INDEXABLES_RAW_COLUMNS); - return true; - } catch (PackageManager.NameNotFoundException e) { - Log.w(LOG_TAG, "Could not create context for " + packageName + ": " - + Log.getStackTraceString(e)); - return false; - } - } - - @VisibleForTesting - void addNonIndexablesKeysFromRemoteProvider(String packageName, - String authority) { - final List keys = - getNonIndexablesKeysFromRemoteProvider(packageName, authority); - - addNonIndexableKeys(packageName, keys); - } - - private List getNonIndexablesKeysFromRemoteProvider(String packageName, - String authority) { - try { - final Context packageContext = mContext.createPackageContext(packageName, 0); - - final Uri uriForNonIndexableKeys = buildUriForNonIndexableKeys(authority); - return getNonIndexablesKeys(packageContext, uriForNonIndexableKeys, - SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS); - } catch (PackageManager.NameNotFoundException e) { - Log.w(LOG_TAG, "Could not create context for " + packageName + ": " - + Log.getStackTraceString(e)); - return EMPTY_LIST; - } - } - - private List getNonIndexablesKeys(Context packageContext, Uri uri, - String[] projection) { - - final ContentResolver resolver = packageContext.getContentResolver(); - final Cursor cursor = resolver.query(uri, projection, null, null, null); - - if (cursor == null) { - Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString()); - return EMPTY_LIST; - } - - final List result = new ArrayList<>(); - try { - final int count = cursor.getCount(); - if (count > 0) { - while (cursor.moveToNext()) { - final String key = cursor.getString(COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE); - - if (TextUtils.isEmpty(key) && Log.isLoggable(LOG_TAG, Log.VERBOSE)) { - Log.v(LOG_TAG, "Empty non-indexable key from: " - + packageContext.getPackageName()); - continue; - } - - result.add(key); - } - } - return result; - } finally { - cursor.close(); - } - } - - public void addIndexableData(SearchIndexableData data) { - synchronized (mDataToProcess) { - mDataToProcess.dataToUpdate.add(data); - } - } - - public void addNonIndexableKeys(String authority, List keys) { - synchronized (mDataToProcess) { - if (keys != null && !keys.isEmpty()) { - mDataToProcess.nonIndexableKeys.put(authority, new ArraySet<>(keys)); - } - } - } /** + * TODO (b/64951285): Deprecate this method + * * Update the Index for a specific class name resources * * @param className the class name (typically a fragment name). @@ -491,9 +372,9 @@ public class DatabaseIndexingManager { AsyncTask.execute(new Runnable() { @Override public void run() { - addIndexableData(res); - updateDatabase(false, Locale.getDefault().toString()); - res.enabled = false; +// addIndexableData(res); +// updateDatabase(false, Locale.getDefault().toString()); +// res.enabled = false; } }); } @@ -507,126 +388,9 @@ public class DatabaseIndexingManager { } } - private static Uri buildUriForXmlResources(String authority) { - return Uri.parse("content://" + authority + "/" + - SearchIndexablesContract.INDEXABLES_XML_RES_PATH); - } - private static Uri buildUriForRawData(String authority) { - return Uri.parse("content://" + authority + "/" + - SearchIndexablesContract.INDEXABLES_RAW_PATH); - } - - private static Uri buildUriForNonIndexableKeys(String authority) { - return Uri.parse("content://" + authority + "/" + - SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH); - } - - private void addIndexablesForXmlResourceUri(Context packageContext, String packageName, - Uri uri, String[] projection) { - - final ContentResolver resolver = packageContext.getContentResolver(); - final Cursor cursor = resolver.query(uri, projection, null, null, null); - - if (cursor == null) { - Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString()); - return; - } - - try { - final int count = cursor.getCount(); - if (count > 0) { - while (cursor.moveToNext()) { - final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID); - - final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME); - final int iconResId = cursor.getInt(COLUMN_INDEX_XML_RES_ICON_RESID); - - final String action = cursor.getString(COLUMN_INDEX_XML_RES_INTENT_ACTION); - final String targetPackage = cursor.getString( - COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE); - final String targetClass = cursor.getString( - COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS); - - SearchIndexableResource sir = new SearchIndexableResource(packageContext); - sir.xmlResId = xmlResId; - sir.className = className; - sir.packageName = packageName; - sir.iconResId = iconResId; - sir.intentAction = action; - sir.intentTargetPackage = targetPackage; - sir.intentTargetClass = targetClass; - - addIndexableData(sir); - } - } - } finally { - cursor.close(); - } - } - - private void addIndexablesForRawDataUri(Context packageContext, String packageName, - Uri uri, String[] projection) { - - final ContentResolver resolver = packageContext.getContentResolver(); - final Cursor cursor = resolver.query(uri, projection, null, null, null); - - if (cursor == null) { - Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString()); - return; - } - - try { - final int count = cursor.getCount(); - if (count > 0) { - while (cursor.moveToNext()) { - final int providerRank = cursor.getInt(COLUMN_INDEX_RAW_RANK); - // TODO Remove rank - final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE); - final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON); - final String summaryOff = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_OFF); - final String entries = cursor.getString(COLUMN_INDEX_RAW_ENTRIES); - final String keywords = cursor.getString(COLUMN_INDEX_RAW_KEYWORDS); - - final String screenTitle = cursor.getString(COLUMN_INDEX_RAW_SCREEN_TITLE); - - final String className = cursor.getString(COLUMN_INDEX_RAW_CLASS_NAME); - final int iconResId = cursor.getInt(COLUMN_INDEX_RAW_ICON_RESID); - - final String action = cursor.getString(COLUMN_INDEX_RAW_INTENT_ACTION); - final String targetPackage = cursor.getString( - COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE); - final String targetClass = cursor.getString( - COLUMN_INDEX_RAW_INTENT_TARGET_CLASS); - - final String key = cursor.getString(COLUMN_INDEX_RAW_KEY); - final int userId = cursor.getInt(COLUMN_INDEX_RAW_USER_ID); - - SearchIndexableRaw data = new SearchIndexableRaw(packageContext); - data.title = title; - data.summaryOn = summaryOn; - data.summaryOff = summaryOff; - data.entries = entries; - data.keywords = keywords; - data.screenTitle = screenTitle; - data.className = className; - data.packageName = packageName; - data.iconResId = iconResId; - data.intentAction = action; - data.intentTargetPackage = targetPackage; - data.intentTargetClass = targetClass; - data.key = key; - data.userId = userId; - - addIndexableData(data); - } - } - } finally { - cursor.close(); - } - } - - public void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr, + @VisibleForTesting + void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr, SearchIndexableData data, Map> nonIndexableKeys) { if (data instanceof SearchIndexableResource) { indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys); @@ -1010,38 +774,6 @@ public class DatabaseIndexingManager { } } - /** - * A private class to describe the indexDatabase data for the Index database - */ - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - static class UpdateData { - public List dataToUpdate; - public List dataToDisable; - public Map> nonIndexableKeys; - - public UpdateData() { - dataToUpdate = new ArrayList<>(); - dataToDisable = new ArrayList<>(); - nonIndexableKeys = new HashMap<>(); - } - - public UpdateData(UpdateData other) { - dataToUpdate = new ArrayList<>(other.dataToUpdate); - dataToDisable = new ArrayList<>(other.dataToDisable); - nonIndexableKeys = new HashMap<>(other.nonIndexableKeys); - } - - public UpdateData copy() { - return new UpdateData(this); - } - - public void clear() { - dataToUpdate.clear(); - dataToDisable.clear(); - nonIndexableKeys.clear(); - } - } - public static class DatabaseRow { public final String locale; public final String updatedTitle; diff --git a/src/com/android/settings/search/DatabaseIndexingUtils.java b/src/com/android/settings/search/DatabaseIndexingUtils.java index 938ddb74b3f..7093134d769 100644 --- a/src/com/android/settings/search/DatabaseIndexingUtils.java +++ b/src/com/android/settings/search/DatabaseIndexingUtils.java @@ -174,35 +174,6 @@ public class DatabaseIndexingUtils { return null; } - /** - * Only allow a "well known" SearchIndexablesProvider. The provider should: - * - * - have read/write {@link Manifest.permission#READ_SEARCH_INDEXABLES} - * - be from a privileged package - */ - static boolean isWellKnownProvider(ResolveInfo info, Context context) { - final String authority = info.providerInfo.authority; - final String packageName = info.providerInfo.applicationInfo.packageName; - - if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(packageName)) { - return false; - } - - final String readPermission = info.providerInfo.readPermission; - final String writePermission = info.providerInfo.writePermission; - - if (TextUtils.isEmpty(readPermission) || TextUtils.isEmpty(writePermission)) { - return false; - } - - if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(readPermission) || - !android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(writePermission)) { - return false; - } - - return isPrivilegedPackage(packageName, context); - } - static String normalizeHyphen(String input) { return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY; } @@ -217,15 +188,4 @@ public class DatabaseIndexingUtils { static String normalizeKeywords(String input) { return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY; } - - private static boolean isPrivilegedPackage(String packageName, Context context) { - final PackageManager pm = context.getPackageManager(); - try { - PackageInfo packInfo = pm.getPackageInfo(packageName, 0); - return ((packInfo.applicationInfo.privateFlags - & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } } diff --git a/src/com/android/settings/search/DatabaseResultLoader.java b/src/com/android/settings/search/DatabaseResultLoader.java index 7815a45ad2c..3d280b2002e 100644 --- a/src/com/android/settings/search/DatabaseResultLoader.java +++ b/src/com/android/settings/search/DatabaseResultLoader.java @@ -40,19 +40,19 @@ public class DatabaseResultLoader extends AsyncLoader providers) { StringBuilder sb = new StringBuilder(); @@ -282,44 +268,42 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper { return sb.toString(); } - static void clearCachedIndexed(Context context) { - context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).edit().clear().commit(); - } - static void setLocaleIndexed(Context context, String locale) { - context.getSharedPreferences(INDEX, Context.MODE_PRIVATE) + context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE) .edit() .putBoolean(locale, true) .apply(); } static void setProvidersIndexed(Context context, String providerVersionedNames) { - context.getSharedPreferences(INDEX, Context.MODE_PRIVATE) + context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE) .edit() .putString(PREF_KEY_INDEXED_PROVIDERS, providerVersionedNames) .apply(); } static boolean isLocaleAlreadyIndexed(Context context, String locale) { - return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).getBoolean(locale, false); + return context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE) + .getBoolean(locale, false); } static boolean areProvidersIndexed(Context context, String providerVersionedNames) { - final String indexedProviders = context.getSharedPreferences(INDEX, Context.MODE_PRIVATE) - .getString(PREF_KEY_INDEXED_PROVIDERS, null); + final String indexedProviders = + context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE) + .getString(PREF_KEY_INDEXED_PROVIDERS, null); return TextUtils.equals(indexedProviders, providerVersionedNames); } static boolean isBuildIndexed(Context context, String buildNo) { - return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).getBoolean(buildNo, false); + return context.getSharedPreferences(SHARED_PREFS_TAG, + Context.MODE_PRIVATE).getBoolean(buildNo, false); } static void setBuildIndexed(Context context, String buildNo) { - context.getSharedPreferences(INDEX, 0).edit().putBoolean(buildNo, true).commit(); + context.getSharedPreferences(SHARED_PREFS_TAG, 0).edit().putBoolean(buildNo, true).commit(); } private void dropTables(SQLiteDatabase db) { - clearCachedIndexed(mContext); db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_META_INDEX); db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_PREFS_INDEX); db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SAVED_QUERIES); diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java index 400cf8fe5f3..69c086f1659 100644 --- a/src/com/android/settings/search/SearchFeatureProviderImpl.java +++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java @@ -74,8 +74,7 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider { @Override public DatabaseIndexingManager getIndexingManager(Context context) { if (mDatabaseIndexingManager == null) { - mDatabaseIndexingManager = new DatabaseIndexingManager(context.getApplicationContext(), - context.getPackageName()); + mDatabaseIndexingManager = new DatabaseIndexingManager(context.getApplicationContext()); } return mDatabaseIndexingManager; } diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java index d67e8eb23a5..044a00c6eb7 100644 --- a/src/com/android/settings/search/SearchIndexableResources.java +++ b/src/com/android/settings/search/SearchIndexableResources.java @@ -44,6 +44,7 @@ import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; import com.android.settings.datausage.DataPlanUsageSummary; import com.android.settings.datausage.DataUsageMeteredSettings; import com.android.settings.datausage.DataUsageSummary; +import com.android.settings.deletionhelper.AutomaticStorageManagerSettings; import com.android.settings.development.DevelopmentSettings; import com.android.settings.deviceinfo.StorageDashboardFragment; import com.android.settings.deviceinfo.StorageSettings; @@ -213,6 +214,10 @@ public final class SearchIndexableResources { R.drawable.ic_settings_notifications); addIndex(DreamSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_display); addIndex(SupportDashboardActivity.class, NO_DATA_RES_ID, R.drawable.ic_help); + addIndex( + AutomaticStorageManagerSettings.class, + NO_DATA_RES_ID, + R.drawable.ic_settings_storage); } private SearchIndexableResources() { diff --git a/src/com/android/settings/search/indexing/IndexableDataCollector.java b/src/com/android/settings/search/indexing/IndexableDataCollector.java new file mode 100644 index 00000000000..cd6b9bbd2c0 --- /dev/null +++ b/src/com/android/settings/search/indexing/IndexableDataCollector.java @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2017 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.settings.search.indexing; + +import android.Manifest; +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.database.Cursor; +import android.net.Uri; +import android.provider.SearchIndexableResource; +import android.provider.SearchIndexablesContract; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.search.SearchIndexableRaw; +import com.android.settings.search.SettingsSearchIndexablesProvider; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID; + +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_RANK; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS; +import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE; + +/** + * Collects all data from {@link android.provider.SearchIndexablesProvider} to be indexed. + */ +public class IndexableDataCollector { + + private static final String TAG = "IndexableDataCollector"; + + // TODO (b/64938328) update to new search package. + private final String BASE_AUTHORITY = "com.android.settings"; + + private static final List EMPTY_LIST = Collections.emptyList(); + + private Context mContext; + + private PreIndexData mIndexData; + + public IndexableDataCollector(Context context) { + mContext = context; + } + + public PreIndexData collectIndexableData(List providers, boolean isFullIndex) { + mIndexData = new PreIndexData(); + + for (final ResolveInfo info : providers) { + if (!isWellKnownProvider(info)) { + continue; + } + final String authority = info.providerInfo.authority; + final String packageName = info.providerInfo.packageName; + + if (isFullIndex) { + addIndexablesFromRemoteProvider(packageName, authority); + } + + final long nonIndexableStartTime = System.currentTimeMillis(); + addNonIndexablesKeysFromRemoteProvider(packageName, authority); + if (SettingsSearchIndexablesProvider.DEBUG) { + final long nonIndexableTime = System.currentTimeMillis() - nonIndexableStartTime; + Log.d(TAG, "performIndexing update non-indexable for package " + packageName + + " took time: " + nonIndexableTime); + } + } + + return mIndexData; + } + + private boolean addIndexablesFromRemoteProvider(String packageName, String authority) { + try { + final Context context = BASE_AUTHORITY.equals(authority) ? + mContext : mContext.createPackageContext(packageName, 0); + + final Uri uriForResources = buildUriForXmlResources(authority); + mIndexData.dataToUpdate.addAll(getIndexablesForXmlResourceUri(context, packageName, + uriForResources, SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS)); + + final Uri uriForRawData = buildUriForRawData(authority); + mIndexData.dataToUpdate.addAll(getIndexablesForRawDataUri(context, packageName, + uriForRawData, SearchIndexablesContract.INDEXABLES_RAW_COLUMNS)); + return true; + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Could not create context for " + packageName + ": " + + Log.getStackTraceString(e)); + return false; + } + } + + @VisibleForTesting + List getIndexablesForXmlResourceUri(Context packageContext, + String packageName, Uri uri, String[] projection) { + + final ContentResolver resolver = packageContext.getContentResolver(); + final Cursor cursor = resolver.query(uri, projection, null, null, null); + List resources = new ArrayList<>(); + + if (cursor == null) { + Log.w(TAG, "Cannot add index data for Uri: " + uri.toString()); + return resources; + } + + try { + final int count = cursor.getCount(); + if (count > 0) { + while (cursor.moveToNext()) { + final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID); + + final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME); + final int iconResId = cursor.getInt(COLUMN_INDEX_XML_RES_ICON_RESID); + + final String action = cursor.getString(COLUMN_INDEX_XML_RES_INTENT_ACTION); + final String targetPackage = cursor.getString( + COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE); + final String targetClass = cursor.getString( + COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS); + + SearchIndexableResource sir = new SearchIndexableResource(packageContext); + sir.xmlResId = xmlResId; + sir.className = className; + sir.packageName = packageName; + sir.iconResId = iconResId; + sir.intentAction = action; + sir.intentTargetPackage = targetPackage; + sir.intentTargetClass = targetClass; + + resources.add(sir); + } + } + } finally { + cursor.close(); + } + return resources; + } + + private void addNonIndexablesKeysFromRemoteProvider(String packageName, + String authority) { + final List keys = + getNonIndexablesKeysFromRemoteProvider(packageName, authority); + + if (keys != null && !keys.isEmpty()) { + mIndexData.nonIndexableKeys.put(authority, new ArraySet<>(keys)); + } + } + + @VisibleForTesting + List getNonIndexablesKeysFromRemoteProvider(String packageName, + String authority) { + try { + final Context packageContext = mContext.createPackageContext(packageName, 0); + + final Uri uriForNonIndexableKeys = buildUriForNonIndexableKeys(authority); + return getNonIndexablesKeys(packageContext, uriForNonIndexableKeys, + SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Could not create context for " + packageName + ": " + + Log.getStackTraceString(e)); + return EMPTY_LIST; + } + } + + @VisibleForTesting + Uri buildUriForXmlResources(String authority) { + return Uri.parse("content://" + authority + "/" + + SearchIndexablesContract.INDEXABLES_XML_RES_PATH); + } + + @VisibleForTesting + Uri buildUriForRawData(String authority) { + return Uri.parse("content://" + authority + "/" + + SearchIndexablesContract.INDEXABLES_RAW_PATH); + } + + @VisibleForTesting + Uri buildUriForNonIndexableKeys(String authority) { + return Uri.parse("content://" + authority + "/" + + SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH); + } + + @VisibleForTesting + List getIndexablesForRawDataUri(Context packageContext, String packageName, + Uri uri, String[] projection) { + final ContentResolver resolver = packageContext.getContentResolver(); + final Cursor cursor = resolver.query(uri, projection, null, null, null); + List rawData = new ArrayList<>(); + + if (cursor == null) { + Log.w(TAG, "Cannot add index data for Uri: " + uri.toString()); + return rawData; + } + + try { + final int count = cursor.getCount(); + if (count > 0) { + while (cursor.moveToNext()) { + final int providerRank = cursor.getInt(COLUMN_INDEX_RAW_RANK); + // TODO Remove rank + final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE); + final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON); + final String summaryOff = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_OFF); + final String entries = cursor.getString(COLUMN_INDEX_RAW_ENTRIES); + final String keywords = cursor.getString(COLUMN_INDEX_RAW_KEYWORDS); + + final String screenTitle = cursor.getString(COLUMN_INDEX_RAW_SCREEN_TITLE); + + final String className = cursor.getString(COLUMN_INDEX_RAW_CLASS_NAME); + final int iconResId = cursor.getInt(COLUMN_INDEX_RAW_ICON_RESID); + + final String action = cursor.getString(COLUMN_INDEX_RAW_INTENT_ACTION); + final String targetPackage = cursor.getString( + COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE); + final String targetClass = cursor.getString( + COLUMN_INDEX_RAW_INTENT_TARGET_CLASS); + + final String key = cursor.getString(COLUMN_INDEX_RAW_KEY); + final int userId = cursor.getInt(COLUMN_INDEX_RAW_USER_ID); + + SearchIndexableRaw data = new SearchIndexableRaw(packageContext); + data.title = title; + data.summaryOn = summaryOn; + data.summaryOff = summaryOff; + data.entries = entries; + data.keywords = keywords; + data.screenTitle = screenTitle; + data.className = className; + data.packageName = packageName; + data.iconResId = iconResId; + data.intentAction = action; + data.intentTargetPackage = targetPackage; + data.intentTargetClass = targetClass; + data.key = key; + data.userId = userId; + + rawData.add(data); + } + } + } finally { + cursor.close(); + } + + return rawData; + } + + private List getNonIndexablesKeys(Context packageContext, Uri uri, + String[] projection) { + + final ContentResolver resolver = packageContext.getContentResolver(); + final Cursor cursor = resolver.query(uri, projection, null, null, null); + final List result = new ArrayList<>(); + + if (cursor == null) { + Log.w(TAG, "Cannot add index data for Uri: " + uri.toString()); + return result; + } + + try { + final int count = cursor.getCount(); + if (count > 0) { + while (cursor.moveToNext()) { + final String key = cursor.getString(COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE); + + if (TextUtils.isEmpty(key) && Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Empty non-indexable key from: " + + packageContext.getPackageName()); + continue; + } + + result.add(key); + } + } + return result; + } finally { + cursor.close(); + } + } + + /** + * Only allow a "well known" SearchIndexablesProvider. The provider should: + * + * - have read/write {@link Manifest.permission#READ_SEARCH_INDEXABLES} + * - be from a privileged package + */ + @VisibleForTesting + boolean isWellKnownProvider(ResolveInfo info) { + final String authority = info.providerInfo.authority; + final String packageName = info.providerInfo.applicationInfo.packageName; + + if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(packageName)) { + return false; + } + + final String readPermission = info.providerInfo.readPermission; + final String writePermission = info.providerInfo.writePermission; + + if (TextUtils.isEmpty(readPermission) || TextUtils.isEmpty(writePermission)) { + return false; + } + + if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(readPermission) || + !android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(writePermission)) { + return false; + } + + return isPrivilegedPackage(packageName, mContext); + } + + /** + * @return true if the {@param packageName} is privileged. + */ + private boolean isPrivilegedPackage(String packageName, Context context) { + final PackageManager pm = context.getPackageManager(); + try { + PackageInfo packInfo = pm.getPackageInfo(packageName, 0); + return ((packInfo.applicationInfo.privateFlags + & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } +} diff --git a/src/com/android/settings/search/indexing/PreIndexData.java b/src/com/android/settings/search/indexing/PreIndexData.java new file mode 100644 index 00000000000..de3cf7ca273 --- /dev/null +++ b/src/com/android/settings/search/indexing/PreIndexData.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 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.settings.search.indexing; + +import android.provider.SearchIndexableData; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * Holds Data sources for indexable data. + * TODO (b/33577327) add getters and setters for data. + */ +public class PreIndexData { + public List dataToUpdate; + public Map> nonIndexableKeys; + + public PreIndexData() { + dataToUpdate = new ArrayList<>(); + nonIndexableKeys = new HashMap<>(); + } + + public PreIndexData(PreIndexData other) { + dataToUpdate = new ArrayList<>(other.dataToUpdate); + nonIndexableKeys = new HashMap<>(other.nonIndexableKeys); + } + + public PreIndexData copy() { + return new PreIndexData(this); + } + + public void clear() { + dataToUpdate.clear(); + nonIndexableKeys.clear(); + } +} diff --git a/src/com/android/settings/wifi/ConfigureWifiSettings.java b/src/com/android/settings/wifi/ConfigureWifiSettings.java index aeee6ffa011..43f25f11977 100644 --- a/src/com/android/settings/wifi/ConfigureWifiSettings.java +++ b/src/com/android/settings/wifi/ConfigureWifiSettings.java @@ -78,7 +78,7 @@ public class ConfigureWifiSettings extends DashboardFragment { final NetworkScoreManagerWrapper networkScoreManagerWrapper = new NetworkScoreManagerWrapper(context.getSystemService(NetworkScoreManager.class)); mWifiWakeupPreferenceController = new WifiWakeupPreferenceController( - context, getLifecycle(), networkScoreManagerWrapper); + context, getLifecycle()); mUseOpenWifiPreferenceController = new UseOpenWifiPreferenceController(context, this, networkScoreManagerWrapper, getLifecycle()); final WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index b319703570f..ef6a650a8b0 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -38,6 +38,7 @@ import android.net.wifi.WpsInfo; import android.nfc.NfcAdapter; import android.os.Bundle; import android.os.HandlerThread; +import android.os.PowerManager; import android.os.Process; import android.provider.Settings; import android.support.annotation.VisibleForTesting; @@ -918,9 +919,8 @@ public class WifiSettings extends RestrictedSettingsFragment getContentResolver(), Settings.Global.WIFI_WAKEUP_AVAILABLE, defaultWakeupAvailable) == 1; if (wifiWakeupAvailable) { - boolean wifiWakeupEnabled = Settings.Global.getInt( - getContentResolver(), Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1; - mConfigureWifiSettingsPreference.setSummary(getString(wifiWakeupEnabled + mConfigureWifiSettingsPreference.setSummary(getString( + isWifiWakeupEnabled() ? R.string.wifi_configure_settings_preference_summary_wakeup_on : R.string.wifi_configure_settings_preference_summary_wakeup_off)); } @@ -935,6 +935,20 @@ public class WifiSettings extends RestrictedSettingsFragment } } + private boolean isWifiWakeupEnabled() { + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + ContentResolver contentResolver = getContentResolver(); + return Settings.Global.getInt(contentResolver, + Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1 + && Settings.Global.getInt(contentResolver, + Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1 + && Settings.Global.getInt(contentResolver, + Settings.Global.AIRPLANE_MODE_ON, 0) == 0 + && Settings.Global.getInt(contentResolver, + Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) == 1 + && !powerManager.isPowerSaveMode(); + } + private void setOffMessage() { final CharSequence title = getText(R.string.wifi_empty_list_wifi_off); // Don't use WifiManager.isScanAlwaysAvailable() to check the Wi-Fi scanning mode. Instead, diff --git a/src/com/android/settings/wifi/WifiWakeupPreferenceController.java b/src/com/android/settings/wifi/WifiWakeupPreferenceController.java index 0017a5d7c4a..efb8aa6f84a 100644 --- a/src/com/android/settings/wifi/WifiWakeupPreferenceController.java +++ b/src/com/android/settings/wifi/WifiWakeupPreferenceController.java @@ -44,13 +44,10 @@ public class WifiWakeupPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnResume, OnPause { private static final String KEY_ENABLE_WIFI_WAKEUP = "enable_wifi_wakeup"; - private final NetworkScoreManagerWrapper mNetworkScoreManager; private SettingObserver mSettingObserver; - public WifiWakeupPreferenceController( - Context context, Lifecycle lifecycle, NetworkScoreManagerWrapper networkScoreManager) { + public WifiWakeupPreferenceController(Context context, Lifecycle lifecycle) { super(context); - mNetworkScoreManager = networkScoreManager; lifecycle.addObserver(this); } @@ -116,11 +113,9 @@ public class WifiWakeupPreferenceController extends AbstractPreferenceController boolean networkRecommendationsEnabled = Settings.Global.getInt( mContext.getContentResolver(), Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) == 1; - boolean activeScorerSet = mNetworkScoreManager.getActiveScorerPackage() != null; - enableWifiWakeup.setEnabled( - networkRecommendationsEnabled && wifiScanningEnabled && activeScorerSet); + enableWifiWakeup.setEnabled(networkRecommendationsEnabled && wifiScanningEnabled); - if (!activeScorerSet) { + if (!networkRecommendationsEnabled) { enableWifiWakeup.setSummary(R.string.wifi_wakeup_summary_scoring_disabled); } else if (!wifiScanningEnabled) { enableWifiWakeup.setSummary(R.string.wifi_wakeup_summary_scanning_disabled); diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable index a08536a94d5..e38064a4253 100644 --- a/tests/robotests/assets/grandfather_not_implementing_indexable +++ b/tests/robotests/assets/grandfather_not_implementing_indexable @@ -64,7 +64,6 @@ com.android.settings.notification.AppNotificationSettings com.android.settings.deviceinfo.PrivateVolumeSettings com.android.settings.users.AppRestrictionsFragment com.android.settings.deviceinfo.PrivateVolumeUnmount -com.android.settings.deletionhelper.AutomaticStorageManagerSettings com.android.settings.notification.ZenAccessSettings com.android.settings.accessibility.ToggleFontSizePreferenceFragment com.android.settings.applications.PremiumSmsAccess diff --git a/tests/robotests/src/android/service/oemlock/OemLockManager.java b/tests/robotests/src/android/service/oemlock/OemLockManager.java index 7c015cf5051..c168089be4b 100644 --- a/tests/robotests/src/android/service/oemlock/OemLockManager.java +++ b/tests/robotests/src/android/service/oemlock/OemLockManager.java @@ -32,9 +32,6 @@ public class OemLockManager { return false; } - public boolean canUserAllowOemUnlock() { - return true; - } public boolean isOemUnlockAllowed() { return false; } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java index a1db5de77e8..ac0720a246f 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java @@ -42,6 +42,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -98,11 +99,29 @@ public class BluetoothDevicePreferenceTest { when(mCachedBluetoothDevice.isConnected()).thenReturn(false); when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); when(mCachedBluetoothDevice.startPairing()).thenReturn(true); + when(mCachedBluetoothDevice.hasHumanReadableName()).thenReturn(true); mPreference.onClicked(); verify(mMetricsFeatureProvider).action( mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR); + verify(mMetricsFeatureProvider, never()).action(mContext, + MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES); + } + + @Test + public void onClicked_deviceNotBonded_shouldLogBluetoothPairEventAndPairWithoutNameEvent() { + when(mCachedBluetoothDevice.isConnected()).thenReturn(false); + when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); + when(mCachedBluetoothDevice.startPairing()).thenReturn(true); + when(mCachedBluetoothDevice.hasHumanReadableName()).thenReturn(false); + + mPreference.onClicked(); + + verify(mMetricsFeatureProvider).action( + mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR); + verify(mMetricsFeatureProvider).action(mContext, + MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES); } @Test diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java index 77213f54cfe..68d33545bc1 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java @@ -19,6 +19,7 @@ package com.android.settings.dashboard; import android.support.annotation.NonNull; import android.support.v7.util.DiffUtil; import android.support.v7.util.ListUpdateCallback; +import android.widget.RemoteViews; import com.android.settings.TestConfig; import com.android.settings.dashboard.conditional.AirplaneModeCondition; @@ -35,6 +36,7 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -224,6 +226,28 @@ public class DashboardDataTest { testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData); } + @Test + public void testDiffUtil_typeSuggestedContainer_ResultDataNothingChanged() { + //Build testResultData + final List testResultData = new ArrayList<>(); + testResultData.add(new ListUpdateResult.ResultData( + ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 0, 1)); + Tile tile = new Tile(); + tile.remoteViews = mock(RemoteViews.class); + + DashboardData prevData = new DashboardData.Builder() + .setConditions(null) + .setCategory(null) + .setSuggestions(Arrays.asList(tile)) + .build(); + DashboardData currentData = new DashboardData.Builder() + .setConditions(null) + .setCategory(null) + .setSuggestions(Arrays.asList(tile)) + .build(); + testDiffUtil(prevData, currentData, testResultData); + } + /** * Test when using the * {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback} diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java index 2687714a959..ea160dd355f 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceManager; @@ -158,6 +159,7 @@ public class DashboardFragmentTest { @Test public void tintTileIcon_hasMetadata_shouldReturnIconTintableMetadata() { final Tile tile = new Tile(); + tile.icon = mock(Icon.class); final Bundle metaData = new Bundle(); tile.metaData = metaData; @@ -168,10 +170,19 @@ public class DashboardFragmentTest { assertThat(mTestFragment.tintTileIcon(tile)).isTrue(); } + @Test + public void tintTileIcon_noIcon_shouldReturnFalse() { + final Tile tile = new Tile(); + final Bundle metaData = new Bundle(); + tile.metaData = metaData; + + assertThat(mTestFragment.tintTileIcon(tile)).isFalse(); + } @Test public void tintTileIcon_noMetadata_shouldReturnPackageNameCheck() { final Tile tile = new Tile(); + tile.icon = mock(Icon.class); final Intent intent = new Intent(); tile.intent = intent; diff --git a/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerDescriptionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerDescriptionPreferenceControllerTest.java new file mode 100644 index 00000000000..dd438ffd77b --- /dev/null +++ b/tests/robotests/src/com/android/settings/deletionhelper/AutomaticStorageManagerDescriptionPreferenceControllerTest.java @@ -0,0 +1,83 @@ +package com.android.settings.deletionhelper; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.provider.Settings; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AutomaticStorageManagerDescriptionPreferenceControllerTest { + @Mock private PreferenceScreen mScreen; + @Mock private Preference mPreference; + private AutomaticStorageManagerDescriptionPreferenceController mController; + private Context mContext = RuntimeEnvironment.application; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mController = new AutomaticStorageManagerDescriptionPreferenceController(mContext); + when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); + when(mPreference.getKey()).thenReturn(mController.getPreferenceKey()); + when(mPreference.getContext()).thenReturn(mContext); + } + + @Test + public void displayPreference_asmDisabled_shouldHaveDescription() { + mController.displayPreference(mScreen); + + verify(mPreference).setSummary(eq(R.string.automatic_storage_manager_text)); + } + + @Test + public void displayPreference_asmEnabledButUnused_shouldHaveDescription() { + Settings.Secure.putInt( + mContext.getContentResolver(), + Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, + 1); + + mController.displayPreference(mScreen); + + verify(mPreference).setSummary(eq(R.string.automatic_storage_manager_text)); + } + + @Ignore("Robolectric doesn't do locale switching for date localization -- yet.") + @Test + @Config(qualifiers = "en") + public void displayPreference_asmEnabledAndUsed_shouldHaveDescriptionFilledOut() { + Settings.Secure.putInt( + mContext.getContentResolver(), + Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, + 1); + Settings.Secure.putLong( + mContext.getContentResolver(), + Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED, + 10); + Settings.Secure.putLong( + mContext.getContentResolver(), + Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN, + 43200000); // January 1, 1970 12:00:00 PM to avoid timezone issues. + + mController.displayPreference(mScreen); + + verify(mPreference) + .setSummary(eq("10.00B total made available\n\nLast ran on January 1, 1970")); + } +} diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java index b2d259a1846..a87f563cc9e 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java @@ -20,13 +20,18 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.Activity; import android.os.storage.StorageManager; import android.provider.SearchIndexableResource; +import android.util.SparseArray; +import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper; +import com.android.settings.deviceinfo.storage.StorageAsyncLoader; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; +import com.android.settingslib.deviceinfo.PrivateStorageInfo; import com.android.settingslib.drawer.CategoryKey; import org.junit.Before; @@ -68,6 +73,47 @@ public class StorageDashboardFragmentTest { verify(activity).invalidateOptionsMenu(); } + @Test + public void test_cacheProviderProvidesValuesIfBothCached() { + CachedStorageValuesHelper helper = mock(CachedStorageValuesHelper.class); + PrivateStorageInfo info = new PrivateStorageInfo(0, 0); + when(helper.getCachedPrivateStorageInfo()).thenReturn(info); + SparseArray result = new SparseArray<>(); + when(helper.getCachedAppsStorageResult()).thenReturn(result); + + mFragment.setCachedStorageValuesHelper(helper); + mFragment.initializeCachedValues(); + + assertThat(mFragment.getPrivateStorageInfo()).isEqualTo(info); + assertThat(mFragment.getAppsStorageResult()).isEqualTo(result); + } + + @Test + public void test_cacheProviderDoesntProvideValuesIfAppsMissing() { + CachedStorageValuesHelper helper = mock(CachedStorageValuesHelper.class); + PrivateStorageInfo info = new PrivateStorageInfo(0, 0); + when(helper.getCachedPrivateStorageInfo()).thenReturn(info); + + mFragment.setCachedStorageValuesHelper(helper); + mFragment.initializeCachedValues(); + + assertThat(mFragment.getPrivateStorageInfo()).isNull(); + assertThat(mFragment.getAppsStorageResult()).isNull(); + } + + @Test + public void test_cacheProviderDoesntProvideValuesIfVolumeInfoMissing() { + CachedStorageValuesHelper helper = mock(CachedStorageValuesHelper.class); + SparseArray result = new SparseArray<>(); + when(helper.getCachedAppsStorageResult()).thenReturn(result); + + mFragment.setCachedStorageValuesHelper(helper); + mFragment.initializeCachedValues(); + + assertThat(mFragment.getPrivateStorageInfo()).isNull(); + assertThat(mFragment.getAppsStorageResult()).isNull(); + } + @Test public void testSearchIndexProvider_shouldIndexResource() { final List indexRes = diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelperTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelperTest.java new file mode 100644 index 00000000000..154a7a1afbf --- /dev/null +++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/CachedStorageValuesHelperTest.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2017 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.settings.deviceinfo.storage; + +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.CACHE_APPS_SIZE_KEY; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_APP_BYTES; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_AUDIO_BYTES; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_IMAGE_BYTES; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_TOTAL_BYTES; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_VIDEO_BYTES; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.FREE_BYTES_KEY; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.GAME_APPS_SIZE_KEY; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.MUSIC_APPS_SIZE_KEY; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.OTHER_APPS_SIZE_KEY; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.PHOTO_APPS_SIZE_KEY; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.SHARED_PREFERENCES_NAME; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.TIMESTAMP_KEY; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.TOTAL_BYTES_KEY; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.USER_ID_KEY; +import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.VIDEO_APPS_SIZE_KEY; + +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.SparseArray; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.applications.StorageStatsSource; +import com.android.settingslib.deviceinfo.PrivateStorageInfo; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import static com.google.common.truth.Truth.assertThat; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class CachedStorageValuesHelperTest { + private Context mContext; + + @Mock private CachedStorageValuesHelper.Clock mMockClock; + private CachedStorageValuesHelper mCachedValuesHelper; + private SharedPreferences mSharedPreferences; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application.getApplicationContext(); + mSharedPreferences = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, 0); + mCachedValuesHelper = new CachedStorageValuesHelper(mContext, 0); + mCachedValuesHelper.mClock = mMockClock; + } + + @Test + public void getCachedPrivateStorageInfo_cachedValuesAreLoaded() throws Exception { + when(mMockClock.getCurrentTime()).thenReturn(10001L); + mSharedPreferences + .edit() + .putLong(GAME_APPS_SIZE_KEY, 0) + .putLong(MUSIC_APPS_SIZE_KEY, 10) + .putLong(VIDEO_APPS_SIZE_KEY, 100) + .putLong(PHOTO_APPS_SIZE_KEY, 1000) + .putLong(OTHER_APPS_SIZE_KEY, 10000) + .putLong(CACHE_APPS_SIZE_KEY, 100000) + .putLong(EXTERNAL_TOTAL_BYTES, 2) + .putLong(EXTERNAL_AUDIO_BYTES, 22) + .putLong(EXTERNAL_VIDEO_BYTES, 222) + .putLong(EXTERNAL_IMAGE_BYTES, 2222) + .putLong(EXTERNAL_APP_BYTES, 22222) + .putLong(FREE_BYTES_KEY, 1000L) + .putLong(TOTAL_BYTES_KEY, 6000L) + .putInt(USER_ID_KEY, 0) + .putLong(TIMESTAMP_KEY, 10000L) + .apply(); + + PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo(); + + assertThat(info.freeBytes).isEqualTo(1000L); + assertThat(info.totalBytes).isEqualTo(6000L); + } + + @Test + public void getCachedAppsStorageResult_cachedValuesAreLoaded() throws Exception { + when(mMockClock.getCurrentTime()).thenReturn(10001L); + mSharedPreferences + .edit() + .putLong(GAME_APPS_SIZE_KEY, 1) + .putLong(MUSIC_APPS_SIZE_KEY, 10) + .putLong(VIDEO_APPS_SIZE_KEY, 100) + .putLong(PHOTO_APPS_SIZE_KEY, 1000) + .putLong(OTHER_APPS_SIZE_KEY, 10000) + .putLong(CACHE_APPS_SIZE_KEY, 100000) + .putLong(EXTERNAL_TOTAL_BYTES, 222222) + .putLong(EXTERNAL_AUDIO_BYTES, 22) + .putLong(EXTERNAL_VIDEO_BYTES, 222) + .putLong(EXTERNAL_IMAGE_BYTES, 2222) + .putLong(EXTERNAL_APP_BYTES, 22222) + .putLong(FREE_BYTES_KEY, 1000L) + .putLong(TOTAL_BYTES_KEY, 5000L) + .putInt(USER_ID_KEY, 0) + .putLong(TIMESTAMP_KEY, 10000L) + .apply(); + + SparseArray result = + mCachedValuesHelper.getCachedAppsStorageResult(); + + StorageAsyncLoader.AppsStorageResult primaryResult = result.get(0); + assertThat(primaryResult.gamesSize).isEqualTo(1L); + assertThat(primaryResult.musicAppsSize).isEqualTo(10L); + assertThat(primaryResult.videoAppsSize).isEqualTo(100L); + assertThat(primaryResult.photosAppsSize).isEqualTo(1000L); + assertThat(primaryResult.otherAppsSize).isEqualTo(10000L); + assertThat(primaryResult.cacheSize).isEqualTo(100000L); + assertThat(primaryResult.externalStats.totalBytes).isEqualTo(222222L); + assertThat(primaryResult.externalStats.audioBytes).isEqualTo(22L); + assertThat(primaryResult.externalStats.videoBytes).isEqualTo(222L); + assertThat(primaryResult.externalStats.imageBytes).isEqualTo(2222L); + assertThat(primaryResult.externalStats.appBytes).isEqualTo(22222L); + } + + @Test + public void getCachedPrivateStorageInfo_nullIfDataIsStale() throws Exception { + when(mMockClock.getCurrentTime()).thenReturn(10000000L); + mSharedPreferences + .edit() + .putLong(GAME_APPS_SIZE_KEY, 0) + .putLong(MUSIC_APPS_SIZE_KEY, 10) + .putLong(VIDEO_APPS_SIZE_KEY, 100) + .putLong(PHOTO_APPS_SIZE_KEY, 1000) + .putLong(OTHER_APPS_SIZE_KEY, 10000) + .putLong(CACHE_APPS_SIZE_KEY, 100000) + .putLong(EXTERNAL_TOTAL_BYTES, 2) + .putLong(EXTERNAL_AUDIO_BYTES, 22) + .putLong(EXTERNAL_VIDEO_BYTES, 222) + .putLong(EXTERNAL_IMAGE_BYTES, 2222) + .putLong(EXTERNAL_APP_BYTES, 22222) + .putLong(FREE_BYTES_KEY, 1000L) + .putLong(TOTAL_BYTES_KEY, 5000L) + .putInt(USER_ID_KEY, 0) + .putLong(TIMESTAMP_KEY, 10000L) + .apply(); + + PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo(); + assertThat(info).isNull(); + } + + @Test + public void getCachedAppsStorageResult_nullIfDataIsStale() throws Exception { + when(mMockClock.getCurrentTime()).thenReturn(10000000L); + mSharedPreferences + .edit() + .putLong(GAME_APPS_SIZE_KEY, 0) + .putLong(MUSIC_APPS_SIZE_KEY, 10) + .putLong(VIDEO_APPS_SIZE_KEY, 100) + .putLong(PHOTO_APPS_SIZE_KEY, 1000) + .putLong(OTHER_APPS_SIZE_KEY, 10000) + .putLong(CACHE_APPS_SIZE_KEY, 100000) + .putLong(EXTERNAL_TOTAL_BYTES, 2) + .putLong(EXTERNAL_AUDIO_BYTES, 22) + .putLong(EXTERNAL_VIDEO_BYTES, 222) + .putLong(EXTERNAL_IMAGE_BYTES, 2222) + .putLong(EXTERNAL_APP_BYTES, 22222) + .putLong(FREE_BYTES_KEY, 1000L) + .putLong(TOTAL_BYTES_KEY, 5000L) + .putInt(USER_ID_KEY, 0) + .putLong(TIMESTAMP_KEY, 10000L) + .apply(); + + SparseArray result = + mCachedValuesHelper.getCachedAppsStorageResult(); + assertThat(result).isNull(); + } + + @Test + public void getCachedPrivateStorageInfo_nullIfWrongUser() throws Exception { + when(mMockClock.getCurrentTime()).thenReturn(10001L); + mSharedPreferences + .edit() + .putLong(GAME_APPS_SIZE_KEY, 0) + .putLong(MUSIC_APPS_SIZE_KEY, 10) + .putLong(VIDEO_APPS_SIZE_KEY, 100) + .putLong(PHOTO_APPS_SIZE_KEY, 1000) + .putLong(OTHER_APPS_SIZE_KEY, 10000) + .putLong(CACHE_APPS_SIZE_KEY, 100000) + .putLong(EXTERNAL_TOTAL_BYTES, 2) + .putLong(EXTERNAL_AUDIO_BYTES, 22) + .putLong(EXTERNAL_VIDEO_BYTES, 222) + .putLong(EXTERNAL_IMAGE_BYTES, 2222) + .putLong(EXTERNAL_APP_BYTES, 22222) + .putLong(FREE_BYTES_KEY, 1000L) + .putLong(TOTAL_BYTES_KEY, 5000L) + .putInt(USER_ID_KEY, 1) + .putLong(TIMESTAMP_KEY, 10000L) + .apply(); + + PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo(); + assertThat(info).isNull(); + } + + @Test + public void getCachedAppsStorageResult_nullIfWrongUser() throws Exception { + when(mMockClock.getCurrentTime()).thenReturn(10001L); + mSharedPreferences + .edit() + .putLong(GAME_APPS_SIZE_KEY, 0) + .putLong(MUSIC_APPS_SIZE_KEY, 10) + .putLong(VIDEO_APPS_SIZE_KEY, 100) + .putLong(PHOTO_APPS_SIZE_KEY, 1000) + .putLong(OTHER_APPS_SIZE_KEY, 10000) + .putLong(CACHE_APPS_SIZE_KEY, 100000) + .putLong(EXTERNAL_TOTAL_BYTES, 2) + .putLong(EXTERNAL_AUDIO_BYTES, 22) + .putLong(EXTERNAL_VIDEO_BYTES, 222) + .putLong(EXTERNAL_IMAGE_BYTES, 2222) + .putLong(EXTERNAL_APP_BYTES, 22222) + .putLong(FREE_BYTES_KEY, 1000L) + .putLong(TOTAL_BYTES_KEY, 5000L) + .putInt(USER_ID_KEY, 1) + .putLong(TIMESTAMP_KEY, 10000L) + .apply(); + + SparseArray result = + mCachedValuesHelper.getCachedAppsStorageResult(); + assertThat(result).isNull(); + } + + @Test + public void getCachedPrivateStorageInfo_nullIfEmpty() throws Exception { + PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo(); + assertThat(info).isNull(); + } + + @Test + public void getCachedAppsStorageResult_nullIfEmpty() throws Exception { + SparseArray result = + mCachedValuesHelper.getCachedAppsStorageResult(); + assertThat(result).isNull(); + } + + @Test + public void cacheResult_succeeds() throws Exception { + when(mMockClock.getCurrentTime()).thenReturn(10000L); + final StorageStatsSource.ExternalStorageStats externalStats = + new StorageStatsSource.ExternalStorageStats(22222l, 2l, 20L, 200L, 2000L); + final StorageAsyncLoader.AppsStorageResult result = + new StorageAsyncLoader.AppsStorageResult(); + result.gamesSize = 1L; + result.musicAppsSize = 10l; + result.videoAppsSize = 100L; + result.photosAppsSize = 1000L; + result.otherAppsSize = 10000L; + result.cacheSize = 100000l; + result.externalStats = externalStats; + final PrivateStorageInfo info = new PrivateStorageInfo(1000L, 6000L); + + mCachedValuesHelper.cacheResult(info, result); + + assertThat(mSharedPreferences.getLong(GAME_APPS_SIZE_KEY, -1)).isEqualTo(1L); + assertThat(mSharedPreferences.getLong(MUSIC_APPS_SIZE_KEY, -1)).isEqualTo(10L); + assertThat(mSharedPreferences.getLong(VIDEO_APPS_SIZE_KEY, -1)).isEqualTo(100L); + assertThat(mSharedPreferences.getLong(PHOTO_APPS_SIZE_KEY, -1)).isEqualTo(1000L); + assertThat(mSharedPreferences.getLong(OTHER_APPS_SIZE_KEY, -1)).isEqualTo(10000L); + assertThat(mSharedPreferences.getLong(CACHE_APPS_SIZE_KEY, -1)).isEqualTo(100000L); + assertThat(mSharedPreferences.getLong(EXTERNAL_TOTAL_BYTES, -1)).isEqualTo(22222L); + assertThat(mSharedPreferences.getLong(EXTERNAL_AUDIO_BYTES, -1)).isEqualTo(2L); + assertThat(mSharedPreferences.getLong(EXTERNAL_VIDEO_BYTES, -1)).isEqualTo(20L); + assertThat(mSharedPreferences.getLong(EXTERNAL_IMAGE_BYTES, -1)).isEqualTo(200L); + assertThat(mSharedPreferences.getLong(EXTERNAL_APP_BYTES, -1)).isEqualTo(2000L); + assertThat(mSharedPreferences.getLong(FREE_BYTES_KEY, -1)).isEqualTo(1000L); + assertThat(mSharedPreferences.getLong(TOTAL_BYTES_KEY, -1)).isEqualTo(6000L); + assertThat(mSharedPreferences.getInt(USER_ID_KEY, -1)).isEqualTo(0); + assertThat(mSharedPreferences.getLong(TIMESTAMP_KEY, -1)).isEqualTo(10000L); + }; +} diff --git a/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java index 1f5c9be1739..7ea42b8d07b 100644 --- a/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java +++ b/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java @@ -32,6 +32,7 @@ import com.android.settingslib.core.AbstractPreferenceController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; @@ -43,7 +44,7 @@ import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class AssistGestureSettingsTest { - @Mock + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; private FakeFeatureFactory mFakeFeatureFactory; private AssistGestureSettings mSettings; @@ -51,6 +52,7 @@ public class AssistGestureSettingsTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(mContext); mFakeFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); mSettings = new AssistGestureSettings(); } diff --git a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java index 70ed568447d..fff38c59322 100644 --- a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java +++ b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java @@ -17,24 +17,20 @@ package com.android.settings.search; -import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.Intent; @@ -43,19 +39,18 @@ import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.database.Cursor; -import android.database.MatrixCursor; import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; import android.os.Build; +import android.provider.SearchIndexableData; import android.provider.SearchIndexableResource; import android.util.ArrayMap; import com.android.settings.R; import com.android.settings.TestConfig; +import com.android.settings.search.indexing.PreIndexData; import com.android.settings.testutils.DatabaseTestUtils; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.testutils.shadow.ShadowDatabaseIndexingUtils; import com.android.settings.testutils.shadow.ShadowRunnableAsyncTask; import org.junit.After; @@ -67,7 +62,6 @@ import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowContentResolver; import java.util.ArrayList; import java.util.Arrays; @@ -84,8 +78,6 @@ import java.util.Set; sdk = TestConfig.SDK_VERSION, shadows = { ShadowRunnableAsyncTask.class, - ShadowDatabaseIndexingUtils.class, - ShadowContentResolver.class } ) public class DatabaseIndexingManagerTest { @@ -129,6 +121,8 @@ public class DatabaseIndexingManagerTest { private DatabaseIndexingManager mManager; private SQLiteDatabase mDb; + private final List FAKE_PROVIDER_LIST = new ArrayList<>(); + @Mock private PackageManager mPackageManager; @@ -136,10 +130,12 @@ public class DatabaseIndexingManagerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - mManager = spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE)); + mManager = spy(new DatabaseIndexingManager(mContext)); mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase(); doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(FAKE_PROVIDER_LIST).when(mPackageManager) + .queryIntentContentProviders(any(Intent.class), anyInt()); FakeFeatureFactory.setupForTest(mContext); } @@ -755,113 +751,60 @@ public class DatabaseIndexingManagerTest { @Test public void testPerformIndexing_fullIndex_getsDataFromProviders() { - DummyProvider provider = new DummyProvider(); - provider.onCreate(); - ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider); + SearchIndexableRaw rawData = getFakeRaw(); + PreIndexData data = getPreIndexData(rawData); + doReturn(data).when(mManager).getIndexDataFromProviders(anyList(), anyBoolean()); + doReturn(true).when(mManager).isFullIndex(any(Context.class), anyString(), anyString(), + anyString()); - // Test that Indexables are added for Full indexing - when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt())) - .thenReturn(getDummyResolveInfo()); + mManager.performIndexing(); - DatabaseIndexingManager manager = - spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE)); - - manager.performIndexing(); - - verify(manager).addIndexablesFromRemoteProvider(PACKAGE_ONE, AUTHORITY_ONE); - verify(manager).updateDatabase(true /* isFullIndex */, Locale.getDefault().toString()); + verify(mManager).updateDatabase(data, true /* isFullIndex */, + Locale.getDefault().toString()); } @Test - public void testPerformIndexing_incrementalIndex_noDataAdded() { - final List providerInfo = getDummyResolveInfo(); - skipFullIndex(providerInfo); - DummyProvider provider = new DummyProvider(); - provider.onCreate(); - ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider); - // Test that Indexables are added for Full indexing - when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt())) - .thenReturn(providerInfo); - + public void testPerformIndexing_fullIndex_databaseDropped() { + // Initialize the Manager and force rebuild DatabaseIndexingManager manager = - spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE)); - - manager.mDataToProcess.dataToUpdate.clear(); - - manager.performIndexing(); - - verify(manager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(), - anyList(), anyMap()); - verify(manager, times(0)).addIndexablesFromRemoteProvider(PACKAGE_ONE, AUTHORITY_ONE); - verify(manager).updateDataInDatabase(any(SQLiteDatabase.class), anyMap()); - } - - @Test - public void testPerformIndexing_localeChanged_databaseDropped() { - DummyProvider provider = new DummyProvider(); - provider.onCreate(); - ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider); - - // Test that Indexables are added for Full indexing - when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt())) - .thenReturn(getDummyResolveInfo()); - - // Initialize the Manager - DatabaseIndexingManager manager = - spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE)); + spy(new DatabaseIndexingManager(mContext)); + doReturn(false).when(mManager).isFullIndex(any(Context.class), anyString(), anyString(), + anyString()); // Insert data point which will be dropped - final String oldTitle = "This is French"; - insertSpecialCase(oldTitle, true, "key"); - - // Add a data point to be added by the indexing - SearchIndexableRaw raw = new SearchIndexableRaw(mContext); - final String newTitle = "This is English"; - raw.title = newTitle; - manager.mDataToProcess.dataToUpdate.add(raw); + insertSpecialCase("Ceci n'est pas un pipe", true, "oui oui mon ami"); manager.performIndexing(); - // Assert that the New Title is inserted - final Cursor newCursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE data_title = '" + - newTitle + "'", null); - assertThat(newCursor.getCount()).isEqualTo(1); - // Assert that the Old Title is no longer in the database, since it was dropped - final Cursor oldCursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE data_title = '" + - oldTitle + "'", null); + final Cursor oldCursor = mDb.rawQuery("SELECT * FROM prefs_index", null); + assertThat(oldCursor.getCount()).isEqualTo(0); } @Test - public void testPerformIndexing_onOta_FullIndex() { - DummyProvider provider = new DummyProvider(); - provider.onCreate(); - ShadowContentResolver.registerProvider( - AUTHORITY_ONE, provider - ); + public void testPerformIndexing_isfullIndex() { + SearchIndexableRaw rawData = getFakeRaw(); + PreIndexData data = getPreIndexData(rawData); + doReturn(data).when(mManager).getIndexDataFromProviders(anyList(), anyBoolean()); + doReturn(true).when(mManager).isFullIndex(any(Context.class), anyString(), anyString(), + anyString()); - // Test that Indexables are added for Full indexing - when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt())) - .thenReturn(getDummyResolveInfo()); + mManager.performIndexing(); - DatabaseIndexingManager manager = - spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE)); - - manager.performIndexing(); - - verify(manager).updateDatabase(true /* isFullIndex */, Locale.getDefault().toString()); + verify(mManager).updateDatabase(data, true /* isFullIndex */, + Locale.getDefault().toString()); } @Test - public void testPerformIndexing_onPackageChange_shouldFullIndex() { + public void testPerformIndexing_onPackageChange_fullIndex() { final List providers = getDummyResolveInfo(); final String buildNumber = Build.FINGERPRINT; final String locale = Locale.getDefault().toString(); skipFullIndex(providers); // This snapshot is already indexed. Should return false - assertThat(IndexDatabaseHelper.isFullIndex( + assertThat(mManager.isFullIndex( mContext, locale, buildNumber, IndexDatabaseHelper.buildProviderVersionedNames(providers))) .isFalse(); @@ -869,65 +812,46 @@ public class DatabaseIndexingManagerTest { // Change provider version number, this should trigger full index. providers.get(0).providerInfo.applicationInfo.versionCode++; - assertThat(IndexDatabaseHelper.isFullIndex(mContext, locale, buildNumber, + assertThat(mManager.isFullIndex(mContext, locale, buildNumber, IndexDatabaseHelper.buildProviderVersionedNames(providers))) .isTrue(); } @Test public void testPerformIndexing_onOta_buildNumberIsCached() { - DummyProvider provider = new DummyProvider(); - provider.onCreate(); - ShadowContentResolver.registerProvider( - AUTHORITY_ONE, provider - ); + mManager.performIndexing(); - // Test that Indexables are added for Full indexing - when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt())) - .thenReturn(getDummyResolveInfo()); - - DatabaseIndexingManager manager = - spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE)); - - manager.performIndexing(); - - assertThat(IndexDatabaseHelper.getInstance(mContext).isBuildIndexed(mContext, - Build.FINGERPRINT)).isTrue(); + assertThat(IndexDatabaseHelper.isBuildIndexed(mContext, Build.FINGERPRINT)).isTrue(); } @Test public void testFullUpdatedDatabase_noData_addDataToDatabaseNotCalled() { - mManager.updateDatabase(true /* isFullIndex */, localeStr); - mManager.mDataToProcess.dataToUpdate.clear(); + PreIndexData emptydata = new PreIndexData(); + mManager.updateDatabase(emptydata, true /* isFullIndex */, localeStr); verify(mManager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(), anyList(), anyMap()); } - @Test - public void testFullUpdatedDatabase_updatedDataInDatabaseNotCalled() { - mManager.updateDatabase(true /* isFullIndex */, localeStr); - verify(mManager, times(0)).updateDataInDatabase(any(SQLiteDatabase.class), anyMap()); - } - @Test public void testLocaleUpdated_afterIndexing_localeNotAdded() { - mManager.updateDatabase(true /* isFullIndex */, localeStr); - assertThat(IndexDatabaseHelper.getInstance(mContext) - .isLocaleAlreadyIndexed(mContext, localeStr)).isFalse(); + PreIndexData emptydata = new PreIndexData(); + mManager.updateDatabase(emptydata, true /* isFullIndex */, localeStr); + + assertThat(IndexDatabaseHelper.isLocaleAlreadyIndexed(mContext, localeStr)).isFalse(); } @Test public void testLocaleUpdated_afterFullIndexing_localeAdded() { mManager.performIndexing(); - assertThat(IndexDatabaseHelper.getInstance(mContext) - .isLocaleAlreadyIndexed(mContext, localeStr)).isTrue(); + assertThat(IndexDatabaseHelper.isLocaleAlreadyIndexed(mContext, localeStr)).isTrue(); } @Test public void testUpdateDatabase_newEligibleData_addedToDatabase() { // Test that addDataToDatabase is called when dataToUpdate is non-empty - mManager.mDataToProcess.dataToUpdate.add(getFakeRaw()); - mManager.updateDatabase(true /* isFullIndex */, localeStr); + PreIndexData indexData = new PreIndexData(); + indexData.dataToUpdate.add(getFakeRaw()); + mManager.updateDatabase(indexData, true /* isFullIndex */, localeStr); Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); cursor.moveToPosition(0); @@ -1020,8 +944,8 @@ public class DatabaseIndexingManagerTest { @Test public void testEmptyNonIndexableKeys_emptyDataKeyResources_addedToDatabase() { insertSpecialCase(TITLE_ONE, true /* enabled */, null /* dataReferenceKey */); - - mManager.updateDatabase(false, localeStr); + PreIndexData emptydata = new PreIndexData(); + mManager.updateDatabase(emptydata, false /* needsReindexing */, localeStr); Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null); cursor.moveToPosition(0); @@ -1111,46 +1035,6 @@ public class DatabaseIndexingManagerTest { return niks; } - private List getDummyResolveInfo() { - List infoList = new ArrayList<>(); - ResolveInfo info = new ResolveInfo(); - info.providerInfo = new ProviderInfo(); - info.providerInfo.exported = true; - info.providerInfo.authority = AUTHORITY_ONE; - info.providerInfo.packageName = PACKAGE_ONE; - info.providerInfo.applicationInfo = new ApplicationInfo(); - infoList.add(info); - - return infoList; - } - - // TODO move this method and its counterpart in CursorToSearchResultConverterTest into - // a util class with public fields to assert values. - private Cursor getDummyCursor() { - MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS); - final String BLANK = ""; - - ArrayList item = - new ArrayList<>(INDEXABLES_RAW_COLUMNS.length); - item.add("42"); // Rank - item.add(TITLE_ONE); // Title - item.add(BLANK); // Summary on - item.add(BLANK); // summary off - item.add(BLANK); // entries - item.add(BLANK); // keywords - item.add(BLANK); // screen title - item.add(BLANK); // classname - item.add("123"); // Icon - item.add(BLANK); // Intent action - item.add(BLANK); // target package - item.add(BLANK); // target class - item.add(KEY_ONE); // Key - item.add("-1"); // userId - cursor.addRow(item); - - return cursor; - } - private void insertSpecialCase(String specialCase, boolean enabled, String key) { ContentValues values = new ContentValues(); values.put(IndexDatabaseHelper.IndexColumns.DOCID, specialCase.hashCode()); @@ -1179,43 +1063,22 @@ public class DatabaseIndexingManagerTest { mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values); } - private class DummyProvider extends ContentProvider { + private PreIndexData getPreIndexData(SearchIndexableData fakeData) { + PreIndexData data = new PreIndexData(); + data.dataToUpdate.add(fakeData); + return data; + } - @Override - public boolean onCreate() { - return false; - } + private List getDummyResolveInfo() { + List infoList = new ArrayList<>(); + ResolveInfo info = new ResolveInfo(); + info.providerInfo = new ProviderInfo(); + info.providerInfo.exported = true; + info.providerInfo.authority = AUTHORITY_ONE; + info.providerInfo.packageName = PACKAGE_ONE; + info.providerInfo.applicationInfo = new ApplicationInfo(); + infoList.add(info); - @Override - public Cursor query(@NonNull Uri uri, @Nullable String[] projection, - @Nullable String selection, @Nullable String[] selectionArgs, - @Nullable String sortOrder) { - if (uri.toString().contains("xml")) { - return null; - } - return getDummyCursor(); - } - - @Override - public String getType(@NonNull Uri uri) { - return null; - } - - @Override - public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { - return null; - } - - @Override - public int delete(@NonNull Uri uri, @Nullable String selection, - @Nullable String[] selectionArgs) { - return 0; - } - - @Override - public int update(@NonNull Uri uri, @Nullable ContentValues values, - @Nullable String selection, @Nullable String[] selectionArgs) { - return 0; - } + return infoList; } } diff --git a/tests/robotests/src/com/android/settings/search/indexing/IndexableDataCollectorTest.java b/tests/robotests/src/com/android/settings/search/indexing/IndexableDataCollectorTest.java new file mode 100644 index 00000000000..0f1f34524d4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/search/indexing/IndexableDataCollectorTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2017 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.settings.search.indexing; + +import android.content.ContentResolver; +import android.content.Context; + +import android.content.pm.ApplicationInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.provider.SearchIndexableResource; +import com.android.settings.TestConfig; +import com.android.settings.search.SearchIndexableRaw; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class IndexableDataCollectorTest { + + private final String AUTHORITY_ONE = "authority"; + private final String PACKAGE_ONE = "com.android.settings"; + + @Mock + ContentResolver mResolver; + + Context mContext; + + IndexableDataCollector mDataCollector; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + doReturn(mResolver).when(mContext).getContentResolver(); + //doReturn(mPackageManager).when(mContext).getPackageManager(); + + mDataCollector = spy(new IndexableDataCollector(mContext)); + } + + @Test + public void testCollectIndexableData_addsResourceData() { + final List providerInfo = getDummyResolveInfo(); + doReturn(true).when(mDataCollector).isWellKnownProvider(any(ResolveInfo.class)); + + List resources = getFakeResource(); + doReturn(resources).when(mDataCollector).getIndexablesForXmlResourceUri( + any(Context.class), anyString(), any(Uri.class), any(String[].class)); + + PreIndexData data = mDataCollector.collectIndexableData(providerInfo, + true /* isFullIndex */); + + assertThat(data.dataToUpdate).containsAllIn(resources); + } + + @Test + public void testCollectIndexableData_addsRawData() { + final List providerInfo = getDummyResolveInfo(); + doReturn(true).when(mDataCollector).isWellKnownProvider(any(ResolveInfo.class)); + + List rawData = getFakeRaw(); + doReturn(rawData).when(mDataCollector).getIndexablesForRawDataUri(any(Context.class), + anyString(), any(Uri.class), any(String[].class)); + + + PreIndexData data = mDataCollector.collectIndexableData(providerInfo, + true /* isFullIndex */); + + assertThat(data.dataToUpdate).containsAllIn(rawData); + } + + @Test + public void testCollectIndexableData_addsNonIndexables() { + final List providerInfo = getDummyResolveInfo(); + doReturn(true).when(mDataCollector).isWellKnownProvider(any(ResolveInfo.class)); + + List niks = getFakeNonIndexables(); + + doReturn(niks).when(mDataCollector).getNonIndexablesKeysFromRemoteProvider(anyString(), + anyString()); + + PreIndexData data = mDataCollector.collectIndexableData(providerInfo, + true /* isFullIndex */); + + assertThat(data.nonIndexableKeys.get(AUTHORITY_ONE)).containsAllIn(niks); + } + + private List getDummyResolveInfo() { + List infoList = new ArrayList<>(); + ResolveInfo info = new ResolveInfo(); + info.providerInfo = new ProviderInfo(); + info.providerInfo.exported = true; + info.providerInfo.authority = AUTHORITY_ONE; + info.providerInfo.packageName = PACKAGE_ONE; + info.providerInfo.applicationInfo = new ApplicationInfo(); + infoList.add(info); + + return infoList; + } + + private List getFakeResource() { + List resources = new ArrayList<>(); + final String BLANK = ""; + + SearchIndexableResource sir = new SearchIndexableResource(mContext); + sir.rank = 0; + sir.xmlResId = 0; + sir.className = BLANK; + sir.packageName = BLANK; + sir.iconResId = 0; + sir.intentAction = BLANK; + sir.intentTargetPackage = BLANK; + sir.intentTargetClass = BLANK; + sir.enabled = true; + resources.add(sir); + + return resources; + } + + private List getFakeRaw() { + List rawData = new ArrayList<>(); + + SearchIndexableRaw data = new SearchIndexableRaw(mContext); + data.title = "bront"; + data.key = "brint"; + rawData.add(data); + + return rawData; + } + + private List getFakeNonIndexables() { + List niks = new ArrayList<>(); + niks.add("they're"); + niks.add("good"); + niks.add("dogs"); + niks.add("brent"); + return niks; + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/WifiWakeupPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiWakeupPreferenceControllerTest.java index 8be686e64ea..306b297c69f 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiWakeupPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiWakeupPreferenceControllerTest.java @@ -56,8 +56,6 @@ public class WifiWakeupPreferenceControllerTest { private static final String TEST_SCORER_PACKAGE_NAME = "Test Scorer"; private Context mContext; - @Mock - private NetworkScoreManagerWrapper mNetworkScorer; private WifiWakeupPreferenceController mController; @Before @@ -65,11 +63,11 @@ public class WifiWakeupPreferenceControllerTest { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mController = new WifiWakeupPreferenceController( - mContext, mock(Lifecycle.class), mNetworkScorer); + mContext, mock(Lifecycle.class)); Settings.System.putInt(mContext.getContentResolver(), WIFI_SCAN_ALWAYS_AVAILABLE, 1); + Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1); SettingsShadowResources.overrideResource( com.android.internal.R.integer.config_wifi_wakeup_available, 0); - when(mNetworkScorer.getActiveScorerPackage()).thenReturn(TEST_SCORER_PACKAGE_NAME); } @After @@ -116,9 +114,8 @@ public class WifiWakeupPreferenceControllerTest { } @Test - public void updateState_preferenceSetCheckedAndSetEnabledWhenSettingsAreEnabled() { + public void updateState_preferenceSetCheckedAndSetEnabledWhenWakeupSettingEnabled() { final SwitchPreference preference = mock(SwitchPreference.class); - Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1); Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1); mController.updateState(preference); @@ -129,22 +126,20 @@ public class WifiWakeupPreferenceControllerTest { } @Test - public void updateState_preferenceSetCheckedAndSetEnabledWhenSettingsAreDisabled() { + public void updateState_preferenceSetUncheckedAndSetEnabledWhenWakeupSettingDisabled() { final SwitchPreference preference = mock(SwitchPreference.class); - Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 0); Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 0); mController.updateState(preference); verify(preference).setChecked(false); - verify(preference).setEnabled(false); + verify(preference).setEnabled(true); verify(preference).setSummary(R.string.wifi_wakeup_summary); } @Test public void updateState_preferenceSetUncheckedAndSetDisabledWhenWifiScanningDisabled() { final SwitchPreference preference = mock(SwitchPreference.class); - Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1); Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1); Settings.System.putInt(mContext.getContentResolver(), WIFI_SCAN_ALWAYS_AVAILABLE, 0); @@ -158,9 +153,8 @@ public class WifiWakeupPreferenceControllerTest { @Test public void updateState_preferenceSetUncheckedAndSetDisabledWhenScoringDisabled() { final SwitchPreference preference = mock(SwitchPreference.class); - Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1); Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1); - when(mNetworkScorer.getActiveScorerPackage()).thenReturn(null); + Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 0); mController.updateState(preference);