• 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             if (accessPreference.getDataUsageState() != null) {
198                 accessPreference.getDataUsageState().isDataSaverAllowlisted = allowlisted;
199             }
200             return true;
201         }
202         return false;
203     }
204 
rebuild()205     public void rebuild() {
206         if (!mExtraLoaded) {
207             return;
208         }
209 
210         final ArrayList<AppEntry> apps = mSession.rebuild(mFilter,
211                 ApplicationsState.ALPHA_COMPARATOR);
212         if (apps != null) {
213             onRebuildComplete(apps);
214         }
215     }
216 
removeUselessPrefs(final Set<String> appsKeySet)217     private void removeUselessPrefs(final Set<String> appsKeySet) {
218         final int prefCount = mScreen.getPreferenceCount();
219         String prefKey;
220         if (prefCount > 0) {
221             for (int i = prefCount - 1; i >= 0; i--) {
222                 Preference pref = mScreen.getPreference(i);
223                 prefKey = pref.getKey();
224                 if (!appsKeySet.isEmpty() && appsKeySet.contains(prefKey)) {
225                     continue;
226                 }
227                 mScreen.removePreference(pref);
228             }
229         }
230     }
231 
232     @VisibleForTesting
logSpecialPermissionChange(boolean allowlisted, String packageName)233     void logSpecialPermissionChange(boolean allowlisted, String packageName) {
234         final int logCategory = allowlisted ? SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW
235                 : SettingsEnums.APP_SPECIAL_PERMISSION_UNL_DATA_DENY;
236         FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext,
237                 logCategory, packageName);
238     }
239 
240     @VisibleForTesting
shouldAddPreference(AppEntry app)241     static boolean shouldAddPreference(AppEntry app) {
242         return app != null && UserHandle.isApp(app.info.uid);
243     }
244 }
245