• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
15 package com.android.settings.datausage;
16 
17 import static com.android.settingslib.RestrictedLockUtils.checkIfMeteredDataRestricted;
18 
19 import android.app.Application;
20 import android.content.Context;
21 import android.os.Bundle;
22 import android.os.UserHandle;
23 import android.support.v7.preference.Preference;
24 import android.support.v7.preference.PreferenceViewHolder;
25 import android.view.Menu;
26 import android.view.MenuInflater;
27 import android.view.MenuItem;
28 import android.view.View;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
32 import com.android.settings.R;
33 import com.android.settings.SettingsPreferenceFragment;
34 import com.android.settings.applications.AppStateBaseBridge;
35 import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
36 import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState;
37 import com.android.settings.overlay.FeatureFactory;
38 import com.android.settings.widget.AppSwitchPreference;
39 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
40 import com.android.settingslib.RestrictedPreferenceHelper;
41 import com.android.settingslib.applications.ApplicationsState;
42 import com.android.settingslib.applications.ApplicationsState.AppEntry;
43 import com.android.settingslib.applications.ApplicationsState.AppFilter;
44 
45 import java.util.ArrayList;
46 
47 public class UnrestrictedDataAccess extends SettingsPreferenceFragment
48         implements ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
49         Preference.OnPreferenceChangeListener {
50 
51     private static final int MENU_SHOW_SYSTEM = Menu.FIRST + 42;
52     private static final String EXTRA_SHOW_SYSTEM = "show_system";
53 
54     private ApplicationsState mApplicationsState;
55     private AppStateDataUsageBridge mDataUsageBridge;
56     private ApplicationsState.Session mSession;
57     private DataSaverBackend mDataSaverBackend;
58     private boolean mShowSystem;
59     private boolean mExtraLoaded;
60     private AppFilter mFilter;
61 
62     @Override
onCreate(Bundle icicle)63     public void onCreate(Bundle icicle) {
64         super.onCreate(icicle);
65         setAnimationAllowed(true);
66         mApplicationsState = ApplicationsState.getInstance(
67                 (Application) getContext().getApplicationContext());
68         mDataSaverBackend = new DataSaverBackend(getContext());
69         mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend);
70         mSession = mApplicationsState.newSession(this, getLifecycle());
71         mShowSystem = icicle != null && icicle.getBoolean(EXTRA_SHOW_SYSTEM);
72         mFilter = mShowSystem ? ApplicationsState.FILTER_ALL_ENABLED
73                 : ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER;
74         setHasOptionsMenu(true);
75     }
76 
77     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)78     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
79         menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE,
80                 mShowSystem ? R.string.menu_hide_system : R.string.menu_show_system);
81         super.onCreateOptionsMenu(menu, inflater);
82     }
83 
84     @Override
onOptionsItemSelected(MenuItem item)85     public boolean onOptionsItemSelected(MenuItem item) {
86         switch (item.getItemId()) {
87             case MENU_SHOW_SYSTEM:
88                 mShowSystem = !mShowSystem;
89                 item.setTitle(mShowSystem ? R.string.menu_hide_system : R.string.menu_show_system);
90                 mFilter = mShowSystem ? ApplicationsState.FILTER_ALL_ENABLED
91                         : ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER;
92                 if (mExtraLoaded) {
93                     rebuild();
94                 }
95                 break;
96         }
97         return super.onOptionsItemSelected(item);
98     }
99 
100     @Override
onSaveInstanceState(Bundle outState)101     public void onSaveInstanceState(Bundle outState) {
102         super.onSaveInstanceState(outState);
103         outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
104     }
105 
106     @Override
onViewCreated(View view, Bundle savedInstanceState)107     public void onViewCreated(View view, Bundle savedInstanceState) {
108         super.onViewCreated(view, savedInstanceState);
109         setLoading(true, false);
110     }
111 
112     @Override
onResume()113     public void onResume() {
114         super.onResume();
115         mDataUsageBridge.resume();
116     }
117 
118     @Override
onPause()119     public void onPause() {
120         super.onPause();
121         mDataUsageBridge.pause();
122     }
123 
124     @Override
onDestroy()125     public void onDestroy() {
126         super.onDestroy();
127         mDataUsageBridge.release();
128     }
129 
130     @Override
onExtraInfoUpdated()131     public void onExtraInfoUpdated() {
132         mExtraLoaded = true;
133         rebuild();
134     }
135 
136     @Override
getHelpResource()137     public int getHelpResource() {
138         return R.string.help_url_unrestricted_data_access;
139     }
140 
rebuild()141     private void rebuild() {
142         ArrayList<AppEntry> apps = mSession.rebuild(mFilter, ApplicationsState.ALPHA_COMPARATOR);
143         if (apps != null) {
144             onRebuildComplete(apps);
145         }
146     }
147 
148     @Override
onRunningStateChanged(boolean running)149     public void onRunningStateChanged(boolean running) {
150 
151     }
152 
153     @Override
onPackageListChanged()154     public void onPackageListChanged() {
155 
156     }
157 
158     @Override
onRebuildComplete(ArrayList<AppEntry> apps)159     public void onRebuildComplete(ArrayList<AppEntry> apps) {
160         if (getContext() == null) return;
161         cacheRemoveAllPrefs(getPreferenceScreen());
162         final int N = apps.size();
163         for (int i = 0; i < N; i++) {
164             AppEntry entry = apps.get(i);
165             if (!shouldAddPreference(entry)) {
166                 continue;
167             }
168             String key = entry.info.packageName + "|" + entry.info.uid;
169             AccessPreference preference = (AccessPreference) getCachedPreference(key);
170             if (preference == null) {
171                 preference = new AccessPreference(getPrefContext(), entry);
172                 preference.setKey(key);
173                 preference.setOnPreferenceChangeListener(this);
174                 getPreferenceScreen().addPreference(preference);
175             } else {
176                 preference.setDisabledByAdmin(checkIfMeteredDataRestricted(getContext(),
177                         entry.info.packageName, UserHandle.getUserId(entry.info.uid)));
178                 preference.reuse();
179             }
180             preference.setOrder(i);
181         }
182         setLoading(false, true);
183         removeCachedPrefs(getPreferenceScreen());
184     }
185 
186     @Override
onPackageIconChanged()187     public void onPackageIconChanged() {
188 
189     }
190 
191     @Override
onPackageSizeChanged(String packageName)192     public void onPackageSizeChanged(String packageName) {
193 
194     }
195 
196     @Override
onAllSizesComputed()197     public void onAllSizesComputed() {
198 
199     }
200 
201     @Override
onLauncherInfoChanged()202     public void onLauncherInfoChanged() {
203 
204     }
205 
206     @Override
onLoadEntriesCompleted()207     public void onLoadEntriesCompleted() {
208 
209     }
210 
211     @Override
getMetricsCategory()212     public int getMetricsCategory() {
213         return MetricsEvent.DATA_USAGE_UNRESTRICTED_ACCESS;
214     }
215 
216     @Override
getPreferenceScreenResId()217     protected int getPreferenceScreenResId() {
218         return R.xml.unrestricted_data_access_settings;
219     }
220 
221     @Override
onPreferenceChange(Preference preference, Object newValue)222     public boolean onPreferenceChange(Preference preference, Object newValue) {
223         if (preference instanceof AccessPreference) {
224             AccessPreference accessPreference = (AccessPreference) preference;
225             boolean whitelisted = newValue == Boolean.TRUE;
226             logSpecialPermissionChange(whitelisted, accessPreference.mEntry.info.packageName);
227             mDataSaverBackend.setIsWhitelisted(accessPreference.mEntry.info.uid,
228                     accessPreference.mEntry.info.packageName, whitelisted);
229             accessPreference.mState.isDataSaverWhitelisted = whitelisted;
230             return true;
231         }
232         return false;
233     }
234 
235     @VisibleForTesting
logSpecialPermissionChange(boolean whitelisted, String packageName)236     void logSpecialPermissionChange(boolean whitelisted, String packageName) {
237         int logCategory = whitelisted ? MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW
238                 : MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_DENY;
239         FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
240                 logCategory, packageName);
241     }
242 
243     @VisibleForTesting
shouldAddPreference(AppEntry app)244     boolean shouldAddPreference(AppEntry app) {
245         return app != null && UserHandle.isApp(app.info.uid);
246     }
247 
248     @VisibleForTesting
249     class AccessPreference extends AppSwitchPreference
250             implements DataSaverBackend.Listener {
251         private final AppEntry mEntry;
252         private final DataUsageState mState;
253         private final RestrictedPreferenceHelper mHelper;
254 
AccessPreference(final Context context, AppEntry entry)255         public AccessPreference(final Context context, AppEntry entry) {
256             super(context);
257             setWidgetLayoutResource(R.layout.restricted_switch_widget);
258             mHelper = new RestrictedPreferenceHelper(context, this, null);
259             mEntry = entry;
260             mState = (DataUsageState) mEntry.extraInfo;
261             mEntry.ensureLabel(getContext());
262             setDisabledByAdmin(checkIfMeteredDataRestricted(context, entry.info.packageName,
263                     UserHandle.getUserId(entry.info.uid)));
264             setState();
265             if (mEntry.icon != null) {
266                 setIcon(mEntry.icon);
267             }
268         }
269 
270         @Override
onAttached()271         public void onAttached() {
272             super.onAttached();
273             mDataSaverBackend.addListener(this);
274         }
275 
276         @Override
onDetached()277         public void onDetached() {
278             mDataSaverBackend.remListener(this);
279             super.onDetached();
280         }
281 
282         @Override
onClick()283         protected void onClick() {
284             if (mState.isDataSaverBlacklisted) {
285                 // app is blacklisted, launch App Data Usage screen
286                 AppInfoDashboardFragment.startAppInfoFragment(AppDataUsage.class,
287                     R.string.app_data_usage,
288                     null /* arguments */,
289                     UnrestrictedDataAccess.this,
290                     mEntry);
291             } else {
292                 // app is not blacklisted, let superclass handle toggle switch
293                 super.onClick();
294             }
295         }
296 
297         @Override
performClick()298         public void performClick() {
299             if (!mHelper.performClick()) {
300                 super.performClick();
301             }
302         }
303 
304         // Sets UI state based on whitelist/blacklist status.
setState()305         private void setState() {
306             setTitle(mEntry.label);
307             if (mState != null) {
308                 setChecked(mState.isDataSaverWhitelisted);
309                 if (isDisabledByAdmin()) {
310                     setSummary(R.string.disabled_by_admin);
311                 } else if (mState.isDataSaverBlacklisted) {
312                     setSummary(R.string.restrict_background_blacklisted);
313                 } else {
314                     setSummary("");
315                 }
316             }
317         }
318 
reuse()319         public void reuse() {
320             setState();
321             notifyChanged();
322         }
323 
324         @Override
onBindViewHolder(PreferenceViewHolder holder)325         public void onBindViewHolder(PreferenceViewHolder holder) {
326             if (mEntry.icon == null) {
327                 holder.itemView.post(new Runnable() {
328                     @Override
329                     public void run() {
330                         // Ensure we have an icon before binding.
331                         mApplicationsState.ensureIcon(mEntry);
332                         // This might trigger us to bind again, but it gives an easy way to only
333                         // load the icon once its needed, so its probably worth it.
334                         setIcon(mEntry.icon);
335                     }
336                 });
337             }
338             final boolean disabledByAdmin = isDisabledByAdmin();
339             final View widgetFrame = holder.findViewById(android.R.id.widget_frame);
340             if (disabledByAdmin) {
341                 widgetFrame.setVisibility(View.VISIBLE);
342             } else {
343                 widgetFrame.setVisibility(mState != null && mState.isDataSaverBlacklisted
344                         ? View.INVISIBLE : View.VISIBLE);
345             }
346             super.onBindViewHolder(holder);
347 
348             mHelper.onBindViewHolder(holder);
349             holder.findViewById(R.id.restricted_icon).setVisibility(
350                     disabledByAdmin ? View.VISIBLE : View.GONE);
351             holder.findViewById(android.R.id.switch_widget).setVisibility(
352                     disabledByAdmin ? View.GONE : View.VISIBLE);
353         }
354 
355         @Override
onDataSaverChanged(boolean isDataSaving)356         public void onDataSaverChanged(boolean isDataSaving) {
357         }
358 
359         @Override
onWhitelistStatusChanged(int uid, boolean isWhitelisted)360         public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) {
361             if (mState != null && mEntry.info.uid == uid) {
362                 mState.isDataSaverWhitelisted = isWhitelisted;
363                 reuse();
364             }
365         }
366 
367         @Override
onBlacklistStatusChanged(int uid, boolean isBlacklisted)368         public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) {
369             if (mState != null && mEntry.info.uid == uid) {
370                 mState.isDataSaverBlacklisted = isBlacklisted;
371                 reuse();
372             }
373         }
374 
setDisabledByAdmin(EnforcedAdmin admin)375         public void setDisabledByAdmin(EnforcedAdmin admin) {
376             mHelper.setDisabledByAdmin(admin);
377         }
378 
isDisabledByAdmin()379         public boolean isDisabledByAdmin() {
380             return mHelper.isDisabledByAdmin();
381         }
382 
383         @VisibleForTesting
getEntryForTest()384         public AppEntry getEntryForTest() {
385             return mEntry;
386         }
387     }
388 
389 }
390