1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 package com.android.settings.datausage; 15 16 import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfMeteredDataUsageUserControlDisabled; 17 18 import android.app.Application; 19 import android.app.settings.SettingsEnums; 20 import android.content.Context; 21 import android.os.UserHandle; 22 23 import androidx.annotation.VisibleForTesting; 24 import androidx.preference.Preference; 25 import androidx.preference.PreferenceScreen; 26 27 import com.android.settings.R; 28 import com.android.settings.applications.AppStateBaseBridge; 29 import com.android.settings.core.BasePreferenceController; 30 import com.android.settings.dashboard.DashboardFragment; 31 import com.android.settings.overlay.FeatureFactory; 32 import com.android.settingslib.applications.AppUtils; 33 import com.android.settingslib.applications.ApplicationsState; 34 import com.android.settingslib.applications.ApplicationsState.AppEntry; 35 import com.android.settingslib.applications.ApplicationsState.AppFilter; 36 import com.android.settingslib.core.lifecycle.Lifecycle; 37 import com.android.settingslib.core.lifecycle.LifecycleObserver; 38 import com.android.settingslib.core.lifecycle.events.OnDestroy; 39 import com.android.settingslib.core.lifecycle.events.OnStart; 40 import com.android.settingslib.core.lifecycle.events.OnStop; 41 42 import java.util.ArrayList; 43 import java.util.Set; 44 import java.util.TreeSet; 45 46 public class UnrestrictedDataAccessPreferenceController extends BasePreferenceController implements 47 LifecycleObserver, OnStart, OnStop, OnDestroy, ApplicationsState.Callbacks, 48 AppStateBaseBridge.Callback, Preference.OnPreferenceChangeListener { 49 50 private final ApplicationsState mApplicationsState; 51 private final AppStateDataUsageBridge mDataUsageBridge; 52 private final DataSaverBackend mDataSaverBackend; 53 private ApplicationsState.Session mSession; 54 private AppFilter mFilter; 55 private DashboardFragment mParentFragment; 56 private PreferenceScreen mScreen; 57 private boolean mExtraLoaded; 58 UnrestrictedDataAccessPreferenceController(Context context, String key)59 public UnrestrictedDataAccessPreferenceController(Context context, String key) { 60 super(context, key); 61 mApplicationsState = ApplicationsState.getInstance( 62 (Application) context.getApplicationContext()); 63 mDataSaverBackend = new DataSaverBackend(context); 64 mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend); 65 } 66 setFilter(AppFilter filter)67 public void setFilter(AppFilter filter) { 68 mFilter = filter; 69 } 70 setParentFragment(DashboardFragment parentFragment)71 public void setParentFragment(DashboardFragment parentFragment) { 72 mParentFragment = parentFragment; 73 } 74 setSession(Lifecycle lifecycle)75 public void setSession(Lifecycle lifecycle) { 76 mSession = mApplicationsState.newSession(this, lifecycle); 77 } 78 79 @Override displayPreference(PreferenceScreen screen)80 public void displayPreference(PreferenceScreen screen) { 81 super.displayPreference(screen); 82 mScreen = screen; 83 } 84 85 @Override getAvailabilityStatus()86 public int getAvailabilityStatus() { 87 return mContext.getResources().getBoolean(R.bool.config_show_data_saver) 88 ? AVAILABLE_UNSEARCHABLE 89 : UNSUPPORTED_ON_DEVICE; 90 } 91 92 @Override onStart()93 public void onStart() { 94 mDataUsageBridge.resume(true /* forceLoadAllApps */); 95 } 96 97 @Override onStop()98 public void onStop() { 99 mDataUsageBridge.pause(); 100 } 101 102 @Override onDestroy()103 public void onDestroy() { 104 mDataUsageBridge.release(); 105 } 106 107 @Override onExtraInfoUpdated()108 public void onExtraInfoUpdated() { 109 mExtraLoaded = true; 110 rebuild(); 111 } 112 113 @Override onRunningStateChanged(boolean running)114 public void onRunningStateChanged(boolean running) { 115 116 } 117 118 @Override onPackageListChanged()119 public void onPackageListChanged() { 120 121 } 122 123 @Override onRebuildComplete(ArrayList<AppEntry> apps)124 public void onRebuildComplete(ArrayList<AppEntry> apps) { 125 if (apps == null) { 126 return; 127 } 128 129 // Preload top visible icons of app list. 130 AppUtils.preloadTopIcons(mContext, apps, 131 mContext.getResources().getInteger(R.integer.config_num_visible_app_icons)); 132 133 // Create apps key set for removing useless preferences 134 final Set<String> appsKeySet = new TreeSet<>(); 135 // Add or update preferences 136 final int N = apps.size(); 137 for (int i = 0; i < N; i++) { 138 final AppEntry entry = apps.get(i); 139 if (!shouldAddPreference(entry)) { 140 continue; 141 } 142 final String prefkey = UnrestrictedDataAccessPreference.generateKey(entry); 143 appsKeySet.add(prefkey); 144 UnrestrictedDataAccessPreference preference = 145 (UnrestrictedDataAccessPreference) mScreen.findPreference(prefkey); 146 if (preference == null) { 147 preference = new UnrestrictedDataAccessPreference(mScreen.getContext(), entry, 148 mApplicationsState, mDataSaverBackend, mParentFragment); 149 preference.setOnPreferenceChangeListener(this); 150 mScreen.addPreference(preference); 151 } else { 152 preference.setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled(mContext, 153 entry.info.packageName, UserHandle.getUserId(entry.info.uid))); 154 preference.checkEcmRestrictionAndSetDisabled(entry.info.packageName); 155 preference.updateState(); 156 } 157 preference.setOrder(i); 158 } 159 160 // Remove useless preferences 161 removeUselessPrefs(appsKeySet); 162 } 163 164 @Override onPackageIconChanged()165 public void onPackageIconChanged() { 166 167 } 168 169 @Override onPackageSizeChanged(String packageName)170 public void onPackageSizeChanged(String packageName) { 171 172 } 173 174 @Override onAllSizesComputed()175 public void onAllSizesComputed() { 176 177 } 178 179 @Override onLauncherInfoChanged()180 public void onLauncherInfoChanged() { 181 182 } 183 184 @Override onLoadEntriesCompleted()185 public void onLoadEntriesCompleted() { 186 187 } 188 189 @Override onPreferenceChange(Preference preference, Object newValue)190 public boolean onPreferenceChange(Preference preference, Object newValue) { 191 if (preference instanceof UnrestrictedDataAccessPreference) { 192 final UnrestrictedDataAccessPreference 193 accessPreference = (UnrestrictedDataAccessPreference) preference; 194 boolean allowlisted = newValue == Boolean.TRUE; 195 logSpecialPermissionChange(allowlisted, accessPreference.getEntry().info.packageName); 196 mDataSaverBackend.setIsAllowlisted(accessPreference.getEntry().info.uid, 197 accessPreference.getEntry().info.packageName, allowlisted); 198 if (accessPreference.getDataUsageState() != null) { 199 accessPreference.getDataUsageState().isDataSaverAllowlisted = allowlisted; 200 } 201 return true; 202 } 203 return false; 204 } 205 rebuild()206 public void rebuild() { 207 if (!mExtraLoaded) { 208 return; 209 } 210 211 final ArrayList<AppEntry> apps = mSession.rebuild(mFilter, 212 ApplicationsState.ALPHA_COMPARATOR); 213 if (apps != null) { 214 onRebuildComplete(apps); 215 } 216 } 217 removeUselessPrefs(final Set<String> appsKeySet)218 private void removeUselessPrefs(final Set<String> appsKeySet) { 219 final int prefCount = mScreen.getPreferenceCount(); 220 String prefKey; 221 if (prefCount > 0) { 222 for (int i = prefCount - 1; i >= 0; i--) { 223 Preference pref = mScreen.getPreference(i); 224 prefKey = pref.getKey(); 225 if (!appsKeySet.isEmpty() && appsKeySet.contains(prefKey)) { 226 continue; 227 } 228 mScreen.removePreference(pref); 229 } 230 } 231 } 232 233 @VisibleForTesting logSpecialPermissionChange(boolean allowlisted, String packageName)234 void logSpecialPermissionChange(boolean allowlisted, String packageName) { 235 final int logCategory = allowlisted ? SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW 236 : SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_DENY; 237 FeatureFactory.getFeatureFactory().getMetricsFeatureProvider().action(mContext, 238 logCategory, packageName); 239 } 240 241 @VisibleForTesting shouldAddPreference(AppEntry app)242 static boolean shouldAddPreference(AppEntry app) { 243 return app != null && UserHandle.isApp(app.info.uid); 244 } 245 } 246