From c2ef27faf4afca6c4f6634b5de975a1bdadf0752 Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Thu, 14 Apr 2016 17:53:07 -0700 Subject: [PATCH] Handle blacklisted apps on Data Saver whitelist. When user blacklists an app for background data access, the "Unrestricted data access" list should not display the whitelist toggle option, but rather explain background access is turned off and offer the option to open the app's data usage screen to turn it back on. It still does not handle changed received by DataSaverBackend, so the UI will be stale when the user removes a blacklist and navigates back to the list. BUG: 27481520 Change-Id: I9d0fa9f0180a69ce9bd1417921bf89ec3ba31f01 --- res/values/strings.xml | 3 + .../applications/InstalledAppDetails.java | 15 +++-- .../settings/datausage/AppDataUsage.java | 14 +---- .../datausage/AppStateDataUsageBridge.java | 10 ++- .../settings/datausage/DataSaverBackend.java | 36 ++++++++++- .../settings/datausage/DataSaverSummary.java | 1 + .../datausage/UnrestrictedDataAccess.java | 61 +++++++++++++++---- 7 files changed, 108 insertions(+), 32 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 16ab7082e7e..3eb6d3df240 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7243,6 +7243,9 @@ Unrestricted data access + + Background data is turned off + On diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 7813745085b..367969920a7 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -73,6 +73,7 @@ import android.webkit.IWebViewUpdateService; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; + import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; @@ -81,6 +82,7 @@ import com.android.settings.AppHeader; import com.android.settings.DeviceAdminAdd; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.applications.PermissionsSummaryHelper.PermissionsResultCallback; import com.android.settings.datausage.AppDataUsage; @@ -741,14 +743,19 @@ public class InstalledAppDetails extends AppInfoBase } private void startAppInfoFragment(Class fragment, CharSequence title) { + startAppInfoFragment(fragment, title, this, mAppEntry); + } + + public static void startAppInfoFragment(Class fragment, CharSequence title, + SettingsPreferenceFragment caller, AppEntry appEntry) { // start new fragment to display extended information Bundle args = new Bundle(); - args.putString(ARG_PACKAGE_NAME, mAppEntry.info.packageName); - args.putInt(ARG_PACKAGE_UID, mAppEntry.info.uid); + args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName); + args.putInt(ARG_PACKAGE_UID, appEntry.info.uid); args.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true); - SettingsActivity sa = (SettingsActivity) getActivity(); - sa.startPreferencePanel(fragment.getName(), args, -1, title, this, SUB_INFO_FRAGMENT); + SettingsActivity sa = (SettingsActivity) caller.getActivity(); + sa.startPreferencePanel(fragment.getName(), args, -1, title, caller, SUB_INFO_FRAGMENT); } /* diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java index 3da3bacd26a..037614e1127 100644 --- a/src/com/android/settings/datausage/AppDataUsage.java +++ b/src/com/android/settings/datausage/AppDataUsage.java @@ -216,7 +216,8 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (preference == mRestrictBackground) { - setAppRestrictBackground(!(Boolean) newValue); + mDataSaverBackend.setIsBlacklisted(mAppItem.key, mPackageName, !(Boolean) newValue); + updatePrefs(); // TODO: should have been notified by NPMS instead return true; } else if (preference == mUnrestrictedData) { mDataSaverBackend.setIsWhitelisted(mAppItem.key, mPackageName, (Boolean) newValue); @@ -287,17 +288,6 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; } - private void setAppRestrictBackground(boolean restrictBackground) { - final int uid = mAppItem.key; - services.mPolicyManager.setUidPolicy( - uid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE); - updatePrefs(); // TODO: should have been notified by NPMS instead - if (restrictBackground) { - MetricsLogger.action(getContext(), - MetricsEvent.ACTION_DATA_SAVER_BLACKLIST, mPackageName); - } - } - @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); diff --git a/src/com/android/settings/datausage/AppStateDataUsageBridge.java b/src/com/android/settings/datausage/AppStateDataUsageBridge.java index 1aff49604f2..0b535d04b3a 100644 --- a/src/com/android/settings/datausage/AppStateDataUsageBridge.java +++ b/src/com/android/settings/datausage/AppStateDataUsageBridge.java @@ -37,20 +37,24 @@ public class AppStateDataUsageBridge extends AppStateBaseBridge { final int N = apps.size(); for (int i = 0; i < N; i++) { AppEntry app = apps.get(i); - app.extraInfo = new DataUsageState(mDataSaverBackend.isWhitelisted(app.info.uid)); + app.extraInfo = new DataUsageState(mDataSaverBackend.isWhitelisted(app.info.uid), + mDataSaverBackend.isBlacklisted(app.info.uid)); } } @Override protected void updateExtraInfo(AppEntry app, String pkg, int uid) { - app.extraInfo = new DataUsageState(mDataSaverBackend.isWhitelisted(uid)); + app.extraInfo = new DataUsageState(mDataSaverBackend.isWhitelisted(uid), + mDataSaverBackend.isBlacklisted(uid)); } public static class DataUsageState { public boolean isDataSaverWhitelisted; + public boolean isDataSaverBlacklisted; - public DataUsageState(boolean isDataSaverWhitelisted) { + public DataUsageState(boolean isDataSaverWhitelisted, boolean isDataSaverBlacklisted) { this.isDataSaverWhitelisted = isDataSaverWhitelisted; + this.isDataSaverBlacklisted = isDataSaverBlacklisted; } } } diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java index d72fe3d5b72..55521a8775f 100644 --- a/src/com/android/settings/datausage/DataSaverBackend.java +++ b/src/com/android/settings/datausage/DataSaverBackend.java @@ -29,6 +29,9 @@ import com.android.internal.logging.MetricsProto.MetricsEvent; import java.util.ArrayList; +import static android.net.NetworkPolicyManager.POLICY_NONE; +import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; + public class DataSaverBackend { private static final String TAG = "DataSaverBackend"; @@ -40,6 +43,7 @@ public class DataSaverBackend { private final INetworkPolicyManager mIPolicyManager; private final ArrayList mListeners = new ArrayList<>(); private SparseBooleanArray mWhitelist; + private SparseBooleanArray mBlacklist; // TODO: Staticize into only one. public DataSaverBackend(Context context) { @@ -121,6 +125,35 @@ public class DataSaverBackend { } } + public void refreshBlacklist() { + loadBlacklist(); + } + + public void setIsBlacklisted(int uid, String packageName, boolean blacklisted) { + mPolicyManager.setUidPolicy( + uid, blacklisted ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE); + if (blacklisted) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_DATA_SAVER_BLACKLIST, packageName); + } + } + + public boolean isBlacklisted(int uid) { + if (mBlacklist == null) { + loadBlacklist(); + } + return mBlacklist.get(uid); + } + + private void loadBlacklist() { + mBlacklist = new SparseBooleanArray(); + try { + for (int uid : mIPolicyManager.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) { + mBlacklist.put(uid, true); + } + } catch (RemoteException e) { + } + } + private void handleRestrictBackgroundChanged(boolean isDataSaving) { for (int i = 0; i < mListeners.size(); i++) { mListeners.get(i).onDataSaverChanged(isDataSaving); @@ -129,7 +162,8 @@ public class DataSaverBackend { private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() { @Override - public void onUidRulesChanged(int i, int i1) throws RemoteException { + public void onUidRulesChanged(int uid, int uidRules) throws RemoteException { + // TODO: update UI accordingly } @Override diff --git a/src/com/android/settings/datausage/DataSaverSummary.java b/src/com/android/settings/datausage/DataSaverSummary.java index e8c8cdd0c33..591f2c5b529 100644 --- a/src/com/android/settings/datausage/DataSaverSummary.java +++ b/src/com/android/settings/datausage/DataSaverSummary.java @@ -70,6 +70,7 @@ public class DataSaverSummary extends SettingsPreferenceFragment public void onResume() { super.onResume(); mDataSaverBackend.refreshWhitelist(); + mDataSaverBackend.refreshBlacklist(); mDataSaverBackend.addListener(this); mSession.resume(); mDataUsageBridge.resume(); diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccess.java b/src/com/android/settings/datausage/UnrestrictedDataAccess.java index 36ec050d0ec..c8df0ba4457 100644 --- a/src/com/android/settings/datausage/UnrestrictedDataAccess.java +++ b/src/com/android/settings/datausage/UnrestrictedDataAccess.java @@ -24,10 +24,15 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; + import com.android.internal.logging.MetricsProto.MetricsEvent; +import com.android.settings.AppHeader; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.AppStateBaseBridge; +import com.android.settings.applications.InstalledAppDetails; import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; @@ -40,6 +45,7 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment private static final int MENU_SHOW_SYSTEM = Menu.FIRST + 42; private static final String EXTRA_SHOW_SYSTEM = "show_system"; + private ApplicationsState mApplicationsState; private AppStateDataUsageBridge mDataUsageBridge; private ApplicationsState.Session mSession; @@ -144,11 +150,11 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment } @Override - public void onRebuildComplete(ArrayList apps) { + public void onRebuildComplete(ArrayList apps) { cacheRemoveAllPrefs(getPreferenceScreen()); final int N = apps.size(); for (int i = 0; i < N; i++) { - ApplicationsState.AppEntry entry = apps.get(i); + AppEntry entry = apps.get(i); String key = entry.info.packageName + "|" + entry.info.uid; AccessPreference preference = (AccessPreference) getCachedPreference(key); if (preference == null) { @@ -202,32 +208,60 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment boolean whitelisted = newValue == Boolean.TRUE; mDataSaverBackend.setIsWhitelisted(accessPreference.mEntry.info.uid, accessPreference.mEntry.info.packageName, whitelisted); - ((AppStateDataUsageBridge.DataUsageState) accessPreference.mEntry.extraInfo) - .isDataSaverWhitelisted = whitelisted; + accessPreference.mState.isDataSaverWhitelisted = whitelisted; return true; } return false; } private class AccessPreference extends SwitchPreference { - private final ApplicationsState.AppEntry mEntry; + private final AppEntry mEntry; + private final DataUsageState mState; - public AccessPreference(Context context, ApplicationsState.AppEntry entry) { + public AccessPreference(final Context context, AppEntry entry) { super(context); mEntry = entry; + mState = (DataUsageState) mEntry.extraInfo; mEntry.ensureLabel(getContext()); - setTitle(entry.label); - final DataUsageState state = (DataUsageState) entry.extraInfo; - setChecked(state != null && state.isDataSaverWhitelisted); + setState(); if (mEntry.icon != null) { setIcon(mEntry.icon); } + setOnPreferenceClickListener( new OnPreferenceClickListener() { + + @Override + public boolean onPreferenceClick(Preference pref) { + if (mState.isDataSaverBlacklisted) { + InstalledAppDetails.startAppInfoFragment(AppDataUsage.class, + context.getString(R.string.app_data_usage), + UnrestrictedDataAccess.this, + mEntry); + return false; + } + return true; + }}); + } + + // Sets UI state based on whitelist/blacklist status. + private void setState() { + setTitle(mEntry.label); + // TODO: state is cached, so if blacklist/whitelist changes, it's not updated. + // For example, if the initial state is blacklisted, the user taps the preference, + // removes the blacklist, and then taps back, the state is not refreshed. + // The proper fix for this problem is to implement onUidRulesChanged() on + // DataSaverBackend and update the UI accordingly. + if (mState != null) { + setChecked(mState.isDataSaverWhitelisted); + if (mState.isDataSaverBlacklisted) { + setSummary(R.string.restrict_background_blacklisted); + } + // TODO: might need to reset summary once it listens to onUidRulesChanged() + } } public void reuse() { - setTitle(mEntry.label); - final DataUsageState state = (DataUsageState) mEntry.extraInfo; - setChecked(state != null && state.isDataSaverWhitelisted); + setState(); + notifyChanged(); } @Override @@ -244,7 +278,10 @@ public class UnrestrictedDataAccess extends SettingsPreferenceFragment } }); } + holder.findViewById(android.R.id.widget_frame) + .setVisibility(mState.isDataSaverBlacklisted ? View.INVISIBLE : View.VISIBLE); super.onBindViewHolder(holder); } } + }