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