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.checkIfMeteredDataUsageUserControlDisabled; 17 18 import android.content.Context; 19 import android.graphics.drawable.Drawable; 20 import android.os.UserHandle; 21 import android.view.View; 22 23 import androidx.annotation.NonNull; 24 import androidx.annotation.Nullable; 25 import androidx.annotation.VisibleForTesting; 26 import androidx.preference.PreferenceViewHolder; 27 28 import com.android.settings.R; 29 import com.android.settings.applications.appinfo.AppInfoDashboardFragment; 30 import com.android.settings.dashboard.DashboardFragment; 31 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 32 import com.android.settingslib.RestrictedPreferenceHelper; 33 import com.android.settingslib.RestrictedPreferenceHelperProvider; 34 import com.android.settingslib.applications.AppUtils; 35 import com.android.settingslib.applications.ApplicationsState; 36 import com.android.settingslib.applications.ApplicationsState.AppEntry; 37 import com.android.settingslib.utils.ThreadUtils; 38 import com.android.settingslib.widget.AppSwitchPreference; 39 40 public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements 41 DataSaverBackend.Listener, RestrictedPreferenceHelperProvider { 42 private static final String ECM_SETTING_IDENTIFIER = "android:unrestricted_data_access"; 43 44 private final ApplicationsState mApplicationsState; 45 private final AppEntry mEntry; 46 private final AppStateDataUsageBridge.DataUsageState mDataUsageState; 47 private final DataSaverBackend mDataSaverBackend; 48 private final DashboardFragment mParentFragment; 49 private final RestrictedPreferenceHelper mHelper; 50 private Drawable mCacheIcon; 51 UnrestrictedDataAccessPreference(final Context context, AppEntry entry, ApplicationsState applicationsState, DataSaverBackend dataSaverBackend, DashboardFragment parentFragment)52 public UnrestrictedDataAccessPreference(final Context context, AppEntry entry, 53 ApplicationsState applicationsState, DataSaverBackend dataSaverBackend, 54 DashboardFragment parentFragment) { 55 super(context); 56 mHelper = new RestrictedPreferenceHelper(context, this, null); 57 mEntry = entry; 58 mDataUsageState = (AppStateDataUsageBridge.DataUsageState) mEntry.extraInfo; 59 mEntry.ensureLabel(context); 60 mApplicationsState = applicationsState; 61 mDataSaverBackend = dataSaverBackend; 62 mParentFragment = parentFragment; 63 setDisabledByAdmin(checkIfMeteredDataUsageUserControlDisabled( 64 context, entry.info.packageName, UserHandle.getUserId(entry.info.uid))); 65 mHelper.checkEcmRestrictionAndSetDisabled(ECM_SETTING_IDENTIFIER, entry.info.packageName); 66 updateState(); 67 setKey(generateKey(mEntry)); 68 69 mCacheIcon = AppUtils.getIconFromCache(mEntry); 70 if (mCacheIcon != null) { 71 setIcon(mCacheIcon); 72 } else { 73 // Set empty icon as default. 74 setIcon(R.drawable.empty_icon); 75 } 76 } 77 generateKey(final AppEntry entry)78 static String generateKey(final AppEntry entry) { 79 return entry.info.packageName + "|" + entry.info.uid; 80 } 81 82 @Override getRestrictedPreferenceHelper()83 public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() { 84 return mHelper; 85 } 86 87 @Override onAttached()88 public void onAttached() { 89 super.onAttached(); 90 mDataSaverBackend.addListener(this); 91 } 92 93 @Override onDetached()94 public void onDetached() { 95 mDataSaverBackend.remListener(this); 96 super.onDetached(); 97 } 98 99 @Override onClick()100 protected void onClick() { 101 if (mDataUsageState != null && mDataUsageState.isDataSaverDenylisted) { 102 // app is denylisted, launch App Data Usage screen 103 AppInfoDashboardFragment.startAppInfoFragment(AppDataUsage.class, 104 R.string.data_usage_app_summary_title, 105 null /* arguments */, 106 mParentFragment, 107 mEntry); 108 } else { 109 // app is not denylisted, let superclass handle toggle switch 110 super.onClick(); 111 } 112 } 113 114 @Override performClick()115 public void performClick() { 116 if (!mHelper.performClick()) { 117 super.performClick(); 118 } 119 } 120 121 @Override onBindViewHolder(PreferenceViewHolder holder)122 public void onBindViewHolder(PreferenceViewHolder holder) { 123 if (mCacheIcon == null) { 124 ThreadUtils.postOnBackgroundThread(() -> { 125 final Drawable icon = AppUtils.getIcon(getContext(), mEntry); 126 ThreadUtils.postOnMainThread(() -> { 127 setIcon(icon); 128 mCacheIcon = icon; 129 }); 130 }); 131 } 132 final boolean disabledByAdmin = isDisabledByAdmin(); 133 final View widgetFrame = holder.findViewById(android.R.id.widget_frame); 134 if (disabledByAdmin) { 135 widgetFrame.setVisibility(View.VISIBLE); 136 } else { 137 widgetFrame.setVisibility( 138 mDataUsageState != null && mDataUsageState.isDataSaverDenylisted 139 ? View.INVISIBLE : View.VISIBLE); 140 } 141 super.onBindViewHolder(holder); 142 143 mHelper.onBindViewHolder(holder); 144 } 145 146 @Override onDataSaverChanged(boolean isDataSaving)147 public void onDataSaverChanged(boolean isDataSaving) { 148 } 149 150 @Override onAllowlistStatusChanged(int uid, boolean isAllowlisted)151 public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) { 152 if (mDataUsageState != null && mEntry.info.uid == uid) { 153 mDataUsageState.isDataSaverAllowlisted = isAllowlisted; 154 updateState(); 155 } 156 } 157 158 @Override onDenylistStatusChanged(int uid, boolean isDenylisted)159 public void onDenylistStatusChanged(int uid, boolean isDenylisted) { 160 if (mDataUsageState != null && mEntry.info.uid == uid) { 161 mDataUsageState.isDataSaverDenylisted = isDenylisted; 162 updateState(); 163 } 164 } 165 166 @Nullable getDataUsageState()167 public AppStateDataUsageBridge.DataUsageState getDataUsageState() { 168 return mDataUsageState; 169 } 170 getEntry()171 public AppEntry getEntry() { 172 return mEntry; 173 } 174 isDisabledByAdmin()175 public boolean isDisabledByAdmin() { 176 return mHelper.isDisabledByAdmin(); 177 } 178 179 @VisibleForTesting isDisabledByEcm()180 boolean isDisabledByEcm() { 181 return mHelper.isDisabledByEcm(); 182 } 183 setDisabledByAdmin(EnforcedAdmin admin)184 public void setDisabledByAdmin(EnforcedAdmin admin) { 185 mHelper.setDisabledByAdmin(admin); 186 } 187 188 /** 189 * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this 190 * package. Marks the preference as disabled if so. 191 * @param packageName the package to check the restriction for 192 */ checkEcmRestrictionAndSetDisabled(@onNull String packageName)193 public void checkEcmRestrictionAndSetDisabled(@NonNull String packageName) { 194 mHelper.checkEcmRestrictionAndSetDisabled(ECM_SETTING_IDENTIFIER, packageName); 195 } 196 197 // Sets UI state based on allowlist/denylist status. updateState()198 public void updateState() { 199 setTitle(mEntry.label); 200 if (mDataUsageState != null) { 201 setChecked(mDataUsageState.isDataSaverAllowlisted); 202 if (isDisabledByAdmin()) { 203 setSummary(com.android.settingslib.widget.restricted.R.string.disabled_by_admin); 204 } else if (mDataUsageState.isDataSaverDenylisted) { 205 setSummary(R.string.restrict_background_blocklisted); 206 // If disabled by ECM, the summary is set directly by the switch. 207 } else if (!isDisabledByEcm()) { 208 setSummary(""); 209 } 210 } 211 notifyChanged(); 212 } 213 } 214