1 /* 2 * Copyright (C) 2014 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.systemui.statusbar.policy; 18 19 import static android.net.ConnectivityManager.TYPE_MOBILE; 20 import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; 21 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; 22 import static android.telephony.TelephonyManager.SIM_STATE_READY; 23 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; 24 import static android.text.format.DateUtils.FORMAT_SHOW_DATE; 25 26 import android.content.Context; 27 import android.net.ConnectivityManager; 28 import android.net.INetworkStatsService; 29 import android.net.INetworkStatsSession; 30 import android.net.NetworkPolicy; 31 import android.net.NetworkPolicyManager; 32 import android.net.NetworkStatsHistory; 33 import android.net.NetworkTemplate; 34 import android.os.RemoteException; 35 import android.os.ServiceManager; 36 import android.telephony.SubscriptionManager; 37 import android.telephony.TelephonyManager; 38 import android.text.format.DateUtils; 39 import android.text.format.Time; 40 import android.util.Log; 41 42 import java.util.Date; 43 import java.util.Locale; 44 45 public class MobileDataControllerImpl implements NetworkController.MobileDataController { 46 private static final String TAG = "MobileDataController"; 47 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 48 49 private static final long DEFAULT_WARNING_LEVEL = 2L * 1024 * 1024 * 1024; 50 private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES; 51 private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50); 52 private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter( 53 PERIOD_BUILDER, Locale.getDefault()); 54 55 private final Context mContext; 56 private final TelephonyManager mTelephonyManager; 57 private final ConnectivityManager mConnectivityManager; 58 private final INetworkStatsService mStatsService; 59 private final NetworkPolicyManager mPolicyManager; 60 61 private INetworkStatsSession mSession; 62 private Callback mCallback; 63 private NetworkControllerImpl mNetworkController; 64 65 public MobileDataControllerImpl(Context context) { 66 mContext = context; 67 mTelephonyManager = TelephonyManager.from(context); 68 mConnectivityManager = ConnectivityManager.from(context); 69 mStatsService = INetworkStatsService.Stub.asInterface( 70 ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); 71 mPolicyManager = NetworkPolicyManager.from(mContext); 72 } 73 74 public void setNetworkController(NetworkControllerImpl networkController) { 75 mNetworkController = networkController; 76 } 77 78 private INetworkStatsSession getSession() { 79 if (mSession == null) { 80 try { 81 mSession = mStatsService.openSession(); 82 } catch (RemoteException e) { 83 Log.w(TAG, "Failed to open stats session", e); 84 } catch (RuntimeException e) { 85 Log.w(TAG, "Failed to open stats session", e); 86 } 87 } 88 return mSession; 89 } 90 91 public void setCallback(Callback callback) { 92 mCallback = callback; 93 } 94 95 private DataUsageInfo warn(String msg) { 96 Log.w(TAG, "Failed to get data usage, " + msg); 97 return null; 98 } 99 100 private static Time addMonth(Time t, int months) { 101 final Time rt = new Time(t); 102 rt.set(t.monthDay, t.month + months, t.year); 103 rt.normalize(false); 104 return rt; 105 } 106 107 public DataUsageInfo getDataUsageInfo() { 108 final String subscriberId = getActiveSubscriberId(mContext); 109 if (subscriberId == null) { 110 return warn("no subscriber id"); 111 } 112 final INetworkStatsSession session = getSession(); 113 if (session == null) { 114 return warn("no stats session"); 115 } 116 NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId); 117 template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds()); 118 119 final NetworkPolicy policy = findNetworkPolicy(template); 120 try { 121 final NetworkStatsHistory history = mSession.getHistoryForNetwork(template, FIELDS); 122 final long now = System.currentTimeMillis(); 123 final long start, end; 124 if (policy != null && policy.cycleDay > 0) { 125 // period = determined from cycleDay 126 if (DEBUG) Log.d(TAG, "Cycle day=" + policy.cycleDay + " tz=" 127 + policy.cycleTimezone); 128 final Time nowTime = new Time(policy.cycleTimezone); 129 nowTime.setToNow(); 130 final Time policyTime = new Time(nowTime); 131 policyTime.set(policy.cycleDay, policyTime.month, policyTime.year); 132 policyTime.normalize(false); 133 if (nowTime.after(policyTime)) { 134 start = policyTime.toMillis(false); 135 end = addMonth(policyTime, 1).toMillis(false); 136 } else { 137 start = addMonth(policyTime, -1).toMillis(false); 138 end = policyTime.toMillis(false); 139 } 140 } else { 141 // period = last 4 wks 142 end = now; 143 start = now - DateUtils.WEEK_IN_MILLIS * 4; 144 } 145 final long callStart = System.currentTimeMillis(); 146 final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); 147 final long callEnd = System.currentTimeMillis(); 148 if (DEBUG) Log.d(TAG, String.format("history call from %s to %s now=%s took %sms: %s", 149 new Date(start), new Date(end), new Date(now), callEnd - callStart, 150 historyEntryToString(entry))); 151 if (entry == null) { 152 return warn("no entry data"); 153 } 154 final long totalBytes = entry.rxBytes + entry.txBytes; 155 final DataUsageInfo usage = new DataUsageInfo(); 156 usage.usageLevel = totalBytes; 157 usage.period = formatDateRange(start, end); 158 if (policy != null) { 159 usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0; 160 usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0; 161 } else { 162 usage.warningLevel = DEFAULT_WARNING_LEVEL; 163 } 164 if (usage != null) { 165 usage.carrier = mNetworkController.getMobileNetworkName(); 166 } 167 return usage; 168 } catch (RemoteException e) { 169 return warn("remote call failed"); 170 } 171 } 172 173 private NetworkPolicy findNetworkPolicy(NetworkTemplate template) { 174 if (mPolicyManager == null || template == null) return null; 175 final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies(); 176 if (policies == null) return null; 177 final int N = policies.length; 178 for (int i = 0; i < N; i++) { 179 final NetworkPolicy policy = policies[i]; 180 if (policy != null && template.equals(policy.template)) { 181 return policy; 182 } 183 } 184 return null; 185 } 186 187 private static String historyEntryToString(NetworkStatsHistory.Entry entry) { 188 return entry == null ? null : new StringBuilder("Entry[") 189 .append("bucketDuration=").append(entry.bucketDuration) 190 .append(",bucketStart=").append(entry.bucketStart) 191 .append(",activeTime=").append(entry.activeTime) 192 .append(",rxBytes=").append(entry.rxBytes) 193 .append(",rxPackets=").append(entry.rxPackets) 194 .append(",txBytes=").append(entry.txBytes) 195 .append(",txPackets=").append(entry.txPackets) 196 .append(",operations=").append(entry.operations) 197 .append(']').toString(); 198 } 199 200 public void setMobileDataEnabled(boolean enabled) { 201 Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled); 202 mTelephonyManager.setDataEnabled(enabled); 203 if (mCallback != null) { 204 mCallback.onMobileDataEnabled(enabled); 205 } 206 } 207 208 public boolean isMobileDataSupported() { 209 // require both supported network and ready SIM 210 return mConnectivityManager.isNetworkSupported(TYPE_MOBILE) 211 && mTelephonyManager.getSimState() == SIM_STATE_READY; 212 } 213 214 public boolean isMobileDataEnabled() { 215 return mTelephonyManager.getDataEnabled(); 216 } 217 218 private static String getActiveSubscriberId(Context context) { 219 final TelephonyManager tele = TelephonyManager.from(context); 220 final String actualSubscriberId = tele.getSubscriberId( 221 SubscriptionManager.getDefaultDataSubId()); 222 return actualSubscriberId; 223 } 224 225 private String formatDateRange(long start, long end) { 226 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH; 227 synchronized (PERIOD_BUILDER) { 228 PERIOD_BUILDER.setLength(0); 229 return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null) 230 .toString(); 231 } 232 } 233 234 public interface Callback { 235 void onMobileDataEnabled(boolean enabled); 236 } 237 } 238