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.checkIfMeteredDataRestricted; 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.ApplicationsState; 33 import com.android.settingslib.applications.ApplicationsState.AppEntry; 34 import com.android.settingslib.applications.ApplicationsState.AppFilter; 35 import com.android.settingslib.core.lifecycle.Lifecycle; 36 import com.android.settingslib.core.lifecycle.LifecycleObserver; 37 import com.android.settingslib.core.lifecycle.events.OnDestroy; 38 import com.android.settingslib.core.lifecycle.events.OnStart; 39 import com.android.settingslib.core.lifecycle.events.OnStop; 40 41 import java.util.ArrayList; 42 import java.util.Set; 43 import java.util.TreeSet; 44 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(); 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 // Create apps key set for removing useless preferences 130 final Set<String> appsKeySet = new TreeSet<>(); 131 // Add or update preferences 132 final int N = apps.size(); 133 for (int i = 0; i < N; i++) { 134 final AppEntry entry = apps.get(i); 135 if (!shouldAddPreference(entry)) { 136 continue; 137 } 138 final String prefkey = UnrestrictedDataAccessPreference.generateKey(entry); 139 appsKeySet.add(prefkey); 140 UnrestrictedDataAccessPreference preference = 141 (UnrestrictedDataAccessPreference) mScreen.findPreference(prefkey); 142 if (preference == null) { 143 preference = new UnrestrictedDataAccessPreference(mScreen.getContext(), entry, 144 mApplicationsState, mDataSaverBackend, mParentFragment); 145 preference.setOnPreferenceChangeListener(this); 146 mScreen.addPreference(preference); 147 } else { 148 preference.setDisabledByAdmin(checkIfMeteredDataRestricted(mContext, 149 entry.info.packageName, UserHandle.getUserId(entry.info.uid))); 150 preference.updateState(); 151 } 152 preference.setOrder(i); 153 } 154 155 // Remove useless preferences 156 removeUselessPrefs(appsKeySet); 157 } 158 159 @Override onPackageIconChanged()160 public void onPackageIconChanged() { 161 162 } 163 164 @Override onPackageSizeChanged(String packageName)165 public void onPackageSizeChanged(String packageName) { 166 167 } 168 169 @Override onAllSizesComputed()170 public void onAllSizesComputed() { 171 172 } 173 174 @Override onLauncherInfoChanged()175 public void onLauncherInfoChanged() { 176 177 } 178 179 @Override onLoadEntriesCompleted()180 public void onLoadEntriesCompleted() { 181 182 } 183 184 @Override onPreferenceChange(Preference preference, Object newValue)185 public boolean onPreferenceChange(Preference preference, Object newValue) { 186 if (preference instanceof UnrestrictedDataAccessPreference) { 187 final UnrestrictedDataAccessPreference 188 accessPreference = (UnrestrictedDataAccessPreference) preference; 189 boolean whitelisted = newValue == Boolean.TRUE; 190 logSpecialPermissionChange(whitelisted, accessPreference.getEntry().info.packageName); 191 mDataSaverBackend.setIsWhitelisted(accessPreference.getEntry().info.uid, 192 accessPreference.getEntry().info.packageName, whitelisted); 193 accessPreference.getDataUsageState().isDataSaverWhitelisted = whitelisted; 194 return true; 195 } 196 return false; 197 } 198 rebuild()199 public void rebuild() { 200 if (!mExtraLoaded) { 201 return; 202 } 203 204 final ArrayList<AppEntry> apps = mSession.rebuild(mFilter, 205 ApplicationsState.ALPHA_COMPARATOR); 206 if (apps != null) { 207 onRebuildComplete(apps); 208 } 209 } 210 removeUselessPrefs(final Set<String> appsKeySet)211 private void removeUselessPrefs(final Set<String> appsKeySet) { 212 final int prefCount = mScreen.getPreferenceCount(); 213 String prefKey; 214 if (prefCount > 0) { 215 for (int i = prefCount - 1; i >= 0; i--) { 216 Preference pref = mScreen.getPreference(i); 217 prefKey = pref.getKey(); 218 if (!appsKeySet.isEmpty() && appsKeySet.contains(prefKey)) { 219 continue; 220 } 221 mScreen.removePreference(pref); 222 } 223 } 224 } 225 226 @VisibleForTesting logSpecialPermissionChange(boolean whitelisted, String packageName)227 void logSpecialPermissionChange(boolean whitelisted, String packageName) { 228 final int logCategory = whitelisted ? SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW 229 : SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_DENY; 230 FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext, 231 logCategory, packageName); 232 } 233 234 @VisibleForTesting shouldAddPreference(AppEntry app)235 static boolean shouldAddPreference(AppEntry app) { 236 return app != null && UserHandle.isApp(app.info.uid); 237 } 238 } 239