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 public class UnrestrictedDataAccessPreferenceController extends BasePreferenceController implements 46 LifecycleObserver, OnStart, OnStop, OnDestroy, ApplicationsState.Callbacks, 47 AppStateBaseBridge.Callback, Preference.OnPreferenceChangeListener { 48 49 private final ApplicationsState mApplicationsState; 50 private final AppStateDataUsageBridge mDataUsageBridge; 51 private final DataSaverBackend mDataSaverBackend; 52 private ApplicationsState.Session mSession; 53 private AppFilter mFilter; 54 private DashboardFragment mParentFragment; 55 private PreferenceScreen mScreen; 56 private boolean mExtraLoaded; 57 UnrestrictedDataAccessPreferenceController(Context context, String key)58 public UnrestrictedDataAccessPreferenceController(Context context, String key) { 59 super(context, key); 60 mApplicationsState = ApplicationsState.getInstance( 61 (Application) context.getApplicationContext()); 62 mDataSaverBackend = new DataSaverBackend(context); 63 mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend); 64 } 65 setFilter(AppFilter filter)66 public void setFilter(AppFilter filter) { 67 mFilter = filter; 68 } 69 setParentFragment(DashboardFragment parentFragment)70 public void setParentFragment(DashboardFragment parentFragment) { 71 mParentFragment = parentFragment; 72 } 73 setSession(Lifecycle lifecycle)74 public void setSession(Lifecycle lifecycle) { 75 mSession = mApplicationsState.newSession(this, lifecycle); 76 } 77 78 @Override displayPreference(PreferenceScreen screen)79 public void displayPreference(PreferenceScreen screen) { 80 super.displayPreference(screen); 81 mScreen = screen; 82 } 83 84 @Override getAvailabilityStatus()85 public int getAvailabilityStatus() { 86 return mContext.getResources().getBoolean(R.bool.config_show_data_saver) 87 ? AVAILABLE_UNSEARCHABLE 88 : UNSUPPORTED_ON_DEVICE; 89 } 90 91 @Override onStart()92 public void onStart() { 93 mDataUsageBridge.resume(); 94 } 95 96 @Override onStop()97 public void onStop() { 98 mDataUsageBridge.pause(); 99 } 100 101 @Override onDestroy()102 public void onDestroy() { 103 mDataUsageBridge.release(); 104 } 105 106 @Override onExtraInfoUpdated()107 public void onExtraInfoUpdated() { 108 mExtraLoaded = true; 109 rebuild(); 110 } 111 112 @Override onRunningStateChanged(boolean running)113 public void onRunningStateChanged(boolean running) { 114 115 } 116 117 @Override onPackageListChanged()118 public void onPackageListChanged() { 119 120 } 121 122 @Override onRebuildComplete(ArrayList<AppEntry> apps)123 public void onRebuildComplete(ArrayList<AppEntry> apps) { 124 if (apps == null) { 125 return; 126 } 127 128 // Create apps key set for removing useless preferences 129 final Set<String> appsKeySet = new TreeSet<>(); 130 // Add or update preferences 131 final int N = apps.size(); 132 for (int i = 0; i < N; i++) { 133 final AppEntry entry = apps.get(i); 134 if (!shouldAddPreference(entry)) { 135 continue; 136 } 137 final String prefkey = UnrestrictedDataAccessPreference.generateKey(entry); 138 appsKeySet.add(prefkey); 139 UnrestrictedDataAccessPreference preference = 140 (UnrestrictedDataAccessPreference) mScreen.findPreference(prefkey); 141 if (preference == null) { 142 preference = new UnrestrictedDataAccessPreference(mScreen.getContext(), entry, 143 mApplicationsState, mDataSaverBackend, mParentFragment); 144 preference.setOnPreferenceChangeListener(this); 145 mScreen.addPreference(preference); 146 } else { 147 preference.setDisabledByAdmin(checkIfMeteredDataRestricted(mContext, 148 entry.info.packageName, UserHandle.getUserId(entry.info.uid))); 149 preference.updateState(); 150 } 151 preference.setOrder(i); 152 } 153 154 // Remove useless preferences 155 removeUselessPrefs(appsKeySet); 156 } 157 158 @Override onPackageIconChanged()159 public void onPackageIconChanged() { 160 161 } 162 163 @Override onPackageSizeChanged(String packageName)164 public void onPackageSizeChanged(String packageName) { 165 166 } 167 168 @Override onAllSizesComputed()169 public void onAllSizesComputed() { 170 171 } 172 173 @Override onLauncherInfoChanged()174 public void onLauncherInfoChanged() { 175 176 } 177 178 @Override onLoadEntriesCompleted()179 public void onLoadEntriesCompleted() { 180 181 } 182 183 @Override onPreferenceChange(Preference preference, Object newValue)184 public boolean onPreferenceChange(Preference preference, Object newValue) { 185 if (preference instanceof UnrestrictedDataAccessPreference) { 186 final UnrestrictedDataAccessPreference 187 accessPreference = (UnrestrictedDataAccessPreference) preference; 188 boolean allowlisted = newValue == Boolean.TRUE; 189 logSpecialPermissionChange(allowlisted, accessPreference.getEntry().info.packageName); 190 mDataSaverBackend.setIsAllowlisted(accessPreference.getEntry().info.uid, 191 accessPreference.getEntry().info.packageName, allowlisted); 192 accessPreference.getDataUsageState().isDataSaverAllowlisted = allowlisted; 193 return true; 194 } 195 return false; 196 } 197 rebuild()198 public void rebuild() { 199 if (!mExtraLoaded) { 200 return; 201 } 202 203 final ArrayList<AppEntry> apps = mSession.rebuild(mFilter, 204 ApplicationsState.ALPHA_COMPARATOR); 205 if (apps != null) { 206 onRebuildComplete(apps); 207 } 208 } 209 removeUselessPrefs(final Set<String> appsKeySet)210 private void removeUselessPrefs(final Set<String> appsKeySet) { 211 final int prefCount = mScreen.getPreferenceCount(); 212 String prefKey; 213 if (prefCount > 0) { 214 for (int i = prefCount - 1; i >= 0; i--) { 215 Preference pref = mScreen.getPreference(i); 216 prefKey = pref.getKey(); 217 if (!appsKeySet.isEmpty() && appsKeySet.contains(prefKey)) { 218 continue; 219 } 220 mScreen.removePreference(pref); 221 } 222 } 223 } 224 225 @VisibleForTesting logSpecialPermissionChange(boolean allowlisted, String packageName)226 void logSpecialPermissionChange(boolean allowlisted, String packageName) { 227 final int logCategory = allowlisted ? SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW 228 : SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_DENY; 229 FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext, 230 logCategory, packageName); 231 } 232 233 @VisibleForTesting shouldAddPreference(AppEntry app)234 static boolean shouldAddPreference(AppEntry app) { 235 return app != null && UserHandle.isApp(app.info.uid); 236 } 237 } 238