• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.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(checkIfMeteredDataRestricted(mContext,
153                         entry.info.packageName, UserHandle.getUserId(entry.info.uid)));
154                 preference.updateState();
155             }
156             preference.setOrder(i);
157         }
158 
159         // Remove useless preferences
160         removeUselessPrefs(appsKeySet);
161     }
162 
163     @Override
onPackageIconChanged()164     public void onPackageIconChanged() {
165 
166     }
167 
168     @Override
onPackageSizeChanged(String packageName)169     public void onPackageSizeChanged(String packageName) {
170 
171     }
172 
173     @Override
onAllSizesComputed()174     public void onAllSizesComputed() {
175 
176     }
177 
178     @Override
onLauncherInfoChanged()179     public void onLauncherInfoChanged() {
180 
181     }
182 
183     @Override
onLoadEntriesCompleted()184     public void onLoadEntriesCompleted() {
185 
186     }
187 
188     @Override
onPreferenceChange(Preference preference, Object newValue)189     public boolean onPreferenceChange(Preference preference, Object newValue) {
190         if (preference instanceof UnrestrictedDataAccessPreference) {
191             final UnrestrictedDataAccessPreference
192                     accessPreference = (UnrestrictedDataAccessPreference) preference;
193             boolean allowlisted = newValue == Boolean.TRUE;
194             logSpecialPermissionChange(allowlisted, accessPreference.getEntry().info.packageName);
195             mDataSaverBackend.setIsAllowlisted(accessPreference.getEntry().info.uid,
196                     accessPreference.getEntry().info.packageName, allowlisted);
197             accessPreference.getDataUsageState().isDataSaverAllowlisted = allowlisted;
198             return true;
199         }
200         return false;
201     }
202 
rebuild()203     public void rebuild() {
204         if (!mExtraLoaded) {
205             return;
206         }
207 
208         final ArrayList<AppEntry> apps = mSession.rebuild(mFilter,
209                 ApplicationsState.ALPHA_COMPARATOR);
210         if (apps != null) {
211             onRebuildComplete(apps);
212         }
213     }
214 
removeUselessPrefs(final Set<String> appsKeySet)215     private void removeUselessPrefs(final Set<String> appsKeySet) {
216         final int prefCount = mScreen.getPreferenceCount();
217         String prefKey;
218         if (prefCount > 0) {
219             for (int i = prefCount - 1; i >= 0; i--) {
220                 Preference pref = mScreen.getPreference(i);
221                 prefKey = pref.getKey();
222                 if (!appsKeySet.isEmpty() && appsKeySet.contains(prefKey)) {
223                     continue;
224                 }
225                 mScreen.removePreference(pref);
226             }
227         }
228     }
229 
230     @VisibleForTesting
logSpecialPermissionChange(boolean allowlisted, String packageName)231     void logSpecialPermissionChange(boolean allowlisted, String packageName) {
232         final int logCategory = allowlisted ? SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW
233                 : SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_DENY;
234         FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext,
235                 logCategory, packageName);
236     }
237 
238     @VisibleForTesting
shouldAddPreference(AppEntry app)239     static boolean shouldAddPreference(AppEntry app) {
240         return app != null && UserHandle.isApp(app.info.uid);
241     }
242 }
243