1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.datausage; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.net.NetworkPolicyManager; 23 import android.net.NetworkTemplate; 24 import android.support.annotation.VisibleForTesting; 25 import android.support.v7.preference.Preference; 26 import android.support.v7.widget.RecyclerView; 27 import android.telephony.SubscriptionInfo; 28 import android.telephony.SubscriptionManager; 29 import android.telephony.SubscriptionPlan; 30 import android.text.TextUtils; 31 import android.util.Log; 32 import android.util.RecurrenceRule; 33 34 import com.android.internal.util.CollectionUtils; 35 import com.android.settings.R; 36 import com.android.settings.core.BasePreferenceController; 37 import com.android.settings.core.PreferenceControllerMixin; 38 import com.android.settings.widget.EntityHeaderController; 39 import com.android.settingslib.NetworkPolicyEditor; 40 import com.android.settingslib.core.lifecycle.Lifecycle; 41 import com.android.settingslib.core.lifecycle.LifecycleObserver; 42 import com.android.settingslib.core.lifecycle.events.OnStart; 43 import com.android.settingslib.net.DataUsageController; 44 45 import java.util.List; 46 47 /** 48 * This is the controller for the top of the data usage screen that retrieves carrier data from the 49 * new subscriptions framework API if available. The controller reads subscription information from 50 * the framework and falls back to legacy usage data if none are available. 51 */ 52 public class DataUsageSummaryPreferenceController extends BasePreferenceController 53 implements PreferenceControllerMixin, LifecycleObserver, OnStart { 54 55 private static final String TAG = "DataUsageController"; 56 private static final String KEY = "status_header"; 57 private static final long PETA = 1000000000000000L; 58 private static final float RELATIVE_SIZE_LARGE = 1.25f * 1.25f; // (1/0.8)^2 59 private static final float RELATIVE_SIZE_SMALL = 1.0f / RELATIVE_SIZE_LARGE; // 0.8^2 60 61 private final Activity mActivity; 62 private final EntityHeaderController mEntityHeaderController; 63 private final Lifecycle mLifecycle; 64 private final DataUsageSummary mDataUsageSummary; 65 private final DataUsageController mDataUsageController; 66 private final DataUsageInfoController mDataInfoController; 67 private final NetworkTemplate mDefaultTemplate; 68 private final NetworkPolicyEditor mPolicyEditor; 69 private final int mDataUsageTemplate; 70 private final boolean mHasMobileData; 71 private final SubscriptionManager mSubscriptionManager; 72 73 /** Name of the carrier, or null if not available */ 74 private CharSequence mCarrierName; 75 76 /** The number of registered plans, [0,N] */ 77 private int mDataplanCount; 78 79 /** The time of the last update in milliseconds since the epoch, or -1 if unknown */ 80 private long mSnapshotTime; 81 82 /** 83 * The size of the first registered plan if one exists or the size of the warning if it is set. 84 * -1 if no information is available. 85 */ 86 private long mDataplanSize; 87 /** The "size" of the data usage bar, i.e. the amount of data its rhs end represents */ 88 private long mDataBarSize; 89 /** The number of bytes used since the start of the cycle. */ 90 private long mDataplanUse; 91 /** The starting time of the billing cycle in ms since the epoch */ 92 private long mCycleStart; 93 /** The ending time of the billing cycle in ms since the epoch */ 94 private long mCycleEnd; 95 96 private Intent mManageSubscriptionIntent; 97 98 public DataUsageSummaryPreferenceController(Activity activity, 99 Lifecycle lifecycle, DataUsageSummary dataUsageSummary) { 100 super(activity, KEY); 101 102 mActivity = activity; 103 mEntityHeaderController = EntityHeaderController.newInstance(activity, 104 dataUsageSummary, null); 105 mLifecycle = lifecycle; 106 mDataUsageSummary = dataUsageSummary; 107 108 final int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(activity); 109 mDefaultTemplate = DataUsageUtils.getDefaultTemplate(activity, defaultSubId); 110 NetworkPolicyManager policyManager = NetworkPolicyManager.from(activity); 111 mPolicyEditor = new NetworkPolicyEditor(policyManager); 112 113 mHasMobileData = DataUsageUtils.hasMobileData(activity) 114 && defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID; 115 116 mDataUsageController = new DataUsageController(activity); 117 mDataInfoController = new DataUsageInfoController(); 118 119 if (mHasMobileData) { 120 mDataUsageTemplate = R.string.cell_data_template; 121 } else if (DataUsageUtils.hasWifiRadio(activity)) { 122 mDataUsageTemplate = R.string.wifi_data_template; 123 } else { 124 mDataUsageTemplate = R.string.ethernet_data_template; 125 } 126 127 mSubscriptionManager = (SubscriptionManager) 128 mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); 129 } 130 131 @VisibleForTesting 132 DataUsageSummaryPreferenceController( 133 DataUsageController dataUsageController, 134 DataUsageInfoController dataInfoController, 135 NetworkTemplate defaultTemplate, 136 NetworkPolicyEditor policyEditor, 137 int dataUsageTemplate, 138 boolean hasMobileData, 139 SubscriptionManager subscriptionManager, 140 Activity activity, 141 Lifecycle lifecycle, 142 EntityHeaderController entityHeaderController, 143 DataUsageSummary dataUsageSummary) { 144 super(activity, KEY); 145 mDataUsageController = dataUsageController; 146 mDataInfoController = dataInfoController; 147 mDefaultTemplate = defaultTemplate; 148 mPolicyEditor = policyEditor; 149 mDataUsageTemplate = dataUsageTemplate; 150 mHasMobileData = hasMobileData; 151 mSubscriptionManager = subscriptionManager; 152 mActivity = activity; 153 mLifecycle = lifecycle; 154 mEntityHeaderController = entityHeaderController; 155 mDataUsageSummary = dataUsageSummary; 156 } 157 158 @Override 159 public void onStart() { 160 RecyclerView view = mDataUsageSummary.getListView(); 161 mEntityHeaderController.setRecyclerView(view, mLifecycle); 162 mEntityHeaderController.styleActionBar(mActivity); 163 } 164 165 @VisibleForTesting 166 void setPlanValues(int dataPlanCount, long dataPlanSize, long dataPlanUse) { 167 mDataplanCount = dataPlanCount; 168 mDataplanSize = dataPlanSize; 169 mDataBarSize = dataPlanSize; 170 mDataplanUse = dataPlanUse; 171 } 172 173 @VisibleForTesting 174 void setCarrierValues(String carrierName, long snapshotTime, long cycleEnd, Intent intent) { 175 mCarrierName = carrierName; 176 mSnapshotTime = snapshotTime; 177 mCycleEnd = cycleEnd; 178 mManageSubscriptionIntent = intent; 179 } 180 181 @Override 182 public int getAvailabilityStatus() { 183 return DataUsageUtils.hasSim(mActivity) 184 || DataUsageUtils.hasWifiRadio(mContext) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; 185 } 186 187 @Override 188 public void updateState(Preference preference) { 189 DataUsageSummaryPreference summaryPreference = (DataUsageSummaryPreference) preference; 190 191 final DataUsageController.DataUsageInfo info; 192 if (DataUsageUtils.hasSim(mActivity)) { 193 info = mDataUsageController.getDataUsageInfo(mDefaultTemplate); 194 mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(mDefaultTemplate)); 195 summaryPreference.setWifiMode(/* isWifiMode */ false, /* usagePeriod */ null); 196 } else { 197 info = mDataUsageController.getDataUsageInfo( 198 NetworkTemplate.buildTemplateWifiWildcard()); 199 summaryPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */ info.period); 200 summaryPreference.setLimitInfo(null); 201 summaryPreference.setUsageNumbers(info.usageLevel, 202 /* dataPlanSize */ -1L, 203 /* hasMobileData */ true); 204 summaryPreference.setChartEnabled(false); 205 summaryPreference.setUsageInfo(info.cycleEnd, 206 /* snapshotTime */ -1L, 207 /* carrierName */ null, 208 /* numPlans */ 0, 209 /* launchIntent */ null); 210 return; 211 } 212 213 if (mSubscriptionManager != null) { 214 refreshDataplanInfo(info); 215 } 216 217 if (info.warningLevel > 0 && info.limitLevel > 0) { 218 summaryPreference.setLimitInfo(TextUtils.expandTemplate( 219 mContext.getText(R.string.cell_data_warning_and_limit), 220 DataUsageUtils.formatDataUsage(mContext, info.warningLevel), 221 DataUsageUtils.formatDataUsage(mContext, info.limitLevel)).toString()); 222 } else if (info.warningLevel > 0) { 223 summaryPreference.setLimitInfo(TextUtils.expandTemplate( 224 mContext.getText(R.string.cell_data_warning), 225 DataUsageUtils.formatDataUsage(mContext, info.warningLevel)).toString()); 226 } else if (info.limitLevel > 0) { 227 summaryPreference.setLimitInfo(TextUtils.expandTemplate( 228 mContext.getText(R.string.cell_data_limit), 229 DataUsageUtils.formatDataUsage(mContext, info.limitLevel)).toString()); 230 } else { 231 summaryPreference.setLimitInfo(null); 232 } 233 234 summaryPreference.setUsageNumbers(mDataplanUse, mDataplanSize, mHasMobileData); 235 236 if (mDataBarSize <= 0) { 237 summaryPreference.setChartEnabled(false); 238 } else { 239 summaryPreference.setChartEnabled(true); 240 summaryPreference.setLabels(DataUsageUtils.formatDataUsage(mContext, 0 /* sizeBytes */), 241 DataUsageUtils.formatDataUsage(mContext, mDataBarSize)); 242 summaryPreference.setProgress(mDataplanUse / (float) mDataBarSize); 243 } 244 summaryPreference.setUsageInfo(mCycleEnd, mSnapshotTime, mCarrierName, 245 mDataplanCount, mManageSubscriptionIntent); 246 } 247 248 // TODO(b/70950124) add test for this method once the robolectric shadow run script is 249 // completed (b/3526807) 250 private void refreshDataplanInfo(DataUsageController.DataUsageInfo info) { 251 // reset data before overwriting 252 mCarrierName = null; 253 mDataplanCount = 0; 254 mDataplanSize = -1L; 255 mDataBarSize = mDataInfoController.getSummaryLimit(info); 256 mDataplanUse = info.usageLevel; 257 mCycleStart = info.cycleStart; 258 mCycleEnd = info.cycleEnd; 259 mSnapshotTime = -1L; 260 261 final int defaultSubId = SubscriptionManager.getDefaultSubscriptionId(); 262 final SubscriptionInfo subInfo = mSubscriptionManager.getDefaultDataSubscriptionInfo(); 263 if (subInfo != null && mHasMobileData) { 264 mCarrierName = subInfo.getCarrierName(); 265 List<SubscriptionPlan> plans = mSubscriptionManager.getSubscriptionPlans(defaultSubId); 266 final SubscriptionPlan primaryPlan = getPrimaryPlan(mSubscriptionManager, defaultSubId); 267 if (primaryPlan != null) { 268 mDataplanCount = plans.size(); 269 mDataplanSize = primaryPlan.getDataLimitBytes(); 270 if (unlimited(mDataplanSize)) { 271 mDataplanSize = -1L; 272 } 273 mDataBarSize = mDataplanSize; 274 mDataplanUse = primaryPlan.getDataUsageBytes(); 275 276 RecurrenceRule rule = primaryPlan.getCycleRule(); 277 if (rule != null && rule.start != null && rule.end != null) { 278 mCycleStart = rule.start.toEpochSecond() * 1000L; 279 mCycleEnd = rule.end.toEpochSecond() * 1000L; 280 } 281 mSnapshotTime = primaryPlan.getDataUsageTime(); 282 } 283 } 284 mManageSubscriptionIntent = 285 mSubscriptionManager.createManageSubscriptionIntent(defaultSubId); 286 Log.i(TAG, "Have " + mDataplanCount + " plans, dflt sub-id " + defaultSubId 287 + ", intent " + mManageSubscriptionIntent); 288 } 289 290 public static SubscriptionPlan getPrimaryPlan(SubscriptionManager subManager, int primaryId) { 291 List<SubscriptionPlan> plans = subManager.getSubscriptionPlans(primaryId); 292 if (CollectionUtils.isEmpty(plans)) { 293 return null; 294 } 295 // First plan in the list is the primary plan 296 SubscriptionPlan plan = plans.get(0); 297 return plan.getDataLimitBytes() > 0 298 && saneSize(plan.getDataUsageBytes()) 299 && plan.getCycleRule() != null ? plan : null; 300 } 301 302 private static boolean saneSize(long value) { 303 return value >= 0L && value < PETA; 304 } 305 306 public static boolean unlimited(long size) { 307 return size == SubscriptionPlan.BYTES_UNLIMITED; 308 } 309 } 310