From 9e696c29f06d45d2891e1d38fd8d9033a9e21bb9 Mon Sep 17 00:00:00 2001 From: Robert Greenwalt Date: Thu, 1 Apr 2010 14:45:18 -0700 Subject: [PATCH] Add service to monitor/control the flow of data. bug:2576057 Change-Id: Ib343c7ee1d619c6978910d9ee597db195d5aa3b6 --- Android.mk | 1 + core/java/android/content/Context.java | 13 +- core/java/android/net/IThrottleManager.aidl | 38 + core/java/android/net/ThrottleManager.java | 193 +++++ core/java/android/provider/Settings.java | 38 +- .../stat_sys_throttle_warning.png | Bin 0 -> 2639 bytes .../res/drawable-hdpi/stat_sys_throttled.png | Bin 0 -> 2503 bytes .../stat_sys_throttle_warning.png | Bin 0 -> 1670 bytes .../res/drawable-mdpi/stat_sys_throttled.png | Bin 0 -> 1519 bytes core/res/res/values/strings.xml | 10 + .../java/com/android/server/SystemServer.java | 12 + .../com/android/server/ThrottleService.java | 729 ++++++++++++++++++ 12 files changed, 1032 insertions(+), 2 deletions(-) create mode 100644 core/java/android/net/IThrottleManager.aidl create mode 100644 core/java/android/net/ThrottleManager.java create mode 100644 core/res/res/drawable-hdpi/stat_sys_throttle_warning.png create mode 100644 core/res/res/drawable-hdpi/stat_sys_throttled.png create mode 100644 core/res/res/drawable-mdpi/stat_sys_throttle_warning.png create mode 100644 core/res/res/drawable-mdpi/stat_sys_throttled.png create mode 100644 services/java/com/android/server/ThrottleService.java diff --git a/Android.mk b/Android.mk index cecc26a447dee..10b6d67562427 100644 --- a/Android.mk +++ b/Android.mk @@ -117,6 +117,7 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/ISensorService.aidl \ core/java/android/net/IConnectivityManager.aidl \ core/java/android/net/INetworkManagementEventObserver.aidl \ + core/java/android/net/IThrottleManager.aidl \ core/java/android/os/IMessenger.aidl \ core/java/android/os/storage/IMountService.aidl \ core/java/android/os/storage/IMountServiceListener.aidl \ diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3a2aa552a0262..30822d4ade1e8 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1398,7 +1398,7 @@ public abstract class Context { * @see android.os.Vibrator */ public static final String VIBRATOR_SERVICE = "vibrator"; - + /** * Use with {@link #getSystemService} to retrieve a {@link * android.app.StatusBarManager} for interacting with the status bar. @@ -1419,6 +1419,17 @@ public abstract class Context { */ public static final String CONNECTIVITY_SERVICE = "connectivity"; + /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.net.ThrottleManager} for handling management of + * throttling. + * + * @hide + * @see #getSystemService + * @see android.net.ThrottleManager + */ + public static final String THROTTLE_SERVICE = "throttle"; + /** * Use with {@link #getSystemService} to retrieve a {@link * android.net.NetworkManagementService} for handling management of diff --git a/core/java/android/net/IThrottleManager.aidl b/core/java/android/net/IThrottleManager.aidl new file mode 100644 index 0000000000000..298de6e50ac61 --- /dev/null +++ b/core/java/android/net/IThrottleManager.aidl @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2010, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.os.IBinder; + +/** + * Interface that answers queries about data transfer amounts and throttling + */ +/** {@hide} */ +interface IThrottleManager +{ + long getByteCount(String iface, int dir, int period, int ago); + + int getThrottle(String iface); + + long getResetTime(String iface); + + long getPeriodStartTime(String iface); + + long getCliffThreshold(String iface, int cliff); + + int getCliffLevel(String iface, int cliff); +} diff --git a/core/java/android/net/ThrottleManager.java b/core/java/android/net/ThrottleManager.java new file mode 100644 index 0000000000000..0500f6f7db9fe --- /dev/null +++ b/core/java/android/net/ThrottleManager.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.os.Binder; +import android.os.RemoteException; + +/** + * Class that handles throttling. It provides read/write numbers per interface + * and methods to apply throttled rates. + * {@hide} + */ +public class ThrottleManager +{ + /** + * Broadcast each polling period to indicate new data counts. + * + * Includes four extras: + * EXTRA_CYCLE_READ - a long of the read bytecount for the current cycle + * EXTRA_CYCLE_WRITE -a long of the write bytecount for the current cycle + * EXTRA_CYLCE_START -a long of MS for the cycle start time + * EXTRA_CYCLE_END -a long of MS for the cycle stop time + * {@hide} + */ + public static final String THROTTLE_POLL_ACTION = "android.net.thrott.POLL_ACTION"; + /** + * The lookup key for a long for the read bytecount for this period. Retrieve with + * {@link android.content.Intent#getLongExtra(String)}. + * {@hide} + */ + public static final String EXTRA_CYCLE_READ = "cycleRead"; + /** + * contains a long of the number of bytes written in the cycle + * {@hide} + */ + public static final String EXTRA_CYCLE_WRITE = "cycleWrite"; + /** + * contains a long of the number of bytes read in the cycle + * {@hide} + */ + public static final String EXTRA_CYCLE_START = "cycleStart"; + /** + * contains a long of the ms since 1970 used to init a calendar, etc for the end + * of the cycle + * {@hide} + */ + public static final String EXTRA_CYCLE_END = "cycleEnd"; + + /** + * Broadcast when the thottle level changes. + * {@hide} + */ + public static final String THROTTLE_ACTION = "android.net.thrott.THROTTLE_ACTION"; + /** + * int of the current bandwidth in TODO + * {@hide} + */ + public static final String EXTRA_THROTTLE_LEVEL = "level"; + + // {@hide} + public static final int DIRECTION_TX = 0; + // {@hide} + public static final int DIRECTION_RX = 1; + + // {@hide} + public static final int PERIOD_CYCLE = 0; + // {@hide} + public static final int PERIOD_YEAR = 1; + // {@hide} + public static final int PERIOD_MONTH = 2; + // {@hide} + public static final int PERIOD_WEEK = 3; + // @hide + public static final int PERIOD_7DAY = 4; + // @hide + public static final int PERIOD_DAY = 5; + // @hide + public static final int PERIOD_24HOUR = 6; + // @hide + public static final int PERIOD_HOUR = 7; + // @hide + public static final int PERIOD_60MIN = 8; + // @hide + public static final int PERIOD_MINUTE = 9; + // @hide + public static final int PERIOD_60SEC = 10; + // @hide + public static final int PERIOD_SECOND = 11; + + /** + * returns a long of the ms from the epoch to the time the current cycle ends for the + * named interface + * {@hide} + */ + public long getResetTime(String iface) { + try { + return mService.getResetTime(iface); + } catch (RemoteException e) { + return -1; + } + } + + /** + * returns a long of the ms from the epoch to the time the current cycle started for the + * named interface + * {@hide} + */ + public long getPeriodStartTime(String iface) { + try { + return mService.getPeriodStartTime(iface); + } catch (RemoteException e) { + return -1; + } + } + + /** + * returns a long of the byte count either read or written on the named interface + * for the period described. Direction is either DIRECTION_RX or DIRECTION_TX and + * period may only be PERIOD_CYCLE for the current cycle (other periods may be supported + * in the future). Ago indicates the number of periods in the past to lookup - 0 means + * the current period, 1 is the last one, 2 was two periods ago.. + * {@hide} + */ + public long getByteCount(String iface, int direction, int period, int ago) { + try { + return mService.getByteCount(iface, direction, period, ago); + } catch (RemoteException e) { + return -1; + } + } + + /** + * returns the number of bytes read+written after which a particular cliff + * takes effect on the named iface. Currently only cliff #0 is supported (1 step) + * {@hide} + */ + public long getCliffThreshold(String iface, int cliff) { + try { + return mService.getCliffThreshold(iface, cliff); + } catch (RemoteException e) { + return -1; + } + } + + /** + * returns the thottling bandwidth (bps) for a given cliff # on the named iface. + * only cliff #0 is currently supported. + * {@hide} + */ + public int getCliffLevel(String iface, int cliff) { + try { + return mService.getCliffLevel(iface, cliff); + } catch (RemoteException e) { + return -1; + } + } + + private IThrottleManager mService; + + /** + * Don't allow use of default constructor. + */ + @SuppressWarnings({"UnusedDeclaration"}) + private ThrottleManager() { + } + + /** + * {@hide} + */ + public ThrottleManager(IThrottleManager service) { + if (service == null) { + throw new IllegalArgumentException( + "ThrottleManager() cannot be constructed with null service"); + } + mService = service; + } +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c07ac31babbae..eb14815c9e6f2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3295,7 +3295,43 @@ public final class Settings { * @hide */ public static final String DEFAULT_INSTALL_LOCATION = "default_install_location"; - + + /** + * The bandwidth throttle polling freqency in seconds + * @hide + */ + public static final String THROTTLE_POLLING_SEC = "throttle_polling_sec"; + + /** + * The bandwidth throttle threshold (long) + * @hide + */ + public static final String THROTTLE_THRESHOLD = "throttle_threshold"; + + /** + * The bandwidth throttle value (kbps) + * @hide + */ + public static final String THROTTLE_VALUE = "throttle_value"; + + /** + * The bandwidth throttle reset calendar day (1-28) + * @hide + */ + public static final String THROTTLE_RESET_DAY = "throttle_reset_day"; + + /** + * The throttling notifications we should send + * @hide + */ + public static final String THROTTLE_NOTIFICATION_TYPE = "throttle_notification_type"; + + /** + * The interface we throttle + * @hide + */ + public static final String THROTTLE_IFACE = "throttle_iface"; + /** * @hide */ diff --git a/core/res/res/drawable-hdpi/stat_sys_throttle_warning.png b/core/res/res/drawable-hdpi/stat_sys_throttle_warning.png new file mode 100644 index 0000000000000000000000000000000000000000..c42b00c495f69ffcee709c51cf3a50286137fe11 GIT binary patch literal 2639 zcmV-V3b6HwP)a z6&tV2?wP$R97v^Ydj1vqX}%N#c_&$VHevMA>V z8K9`^mHD+&LRCqywo|sB3rBEph|F}{ZysR*GaH(UP8yPsDrTI7ptiGCL*lSm$$(FKr zi%R@C21-wKfulv5^*^4$88%0I@$s9VrTKI56$1u0PN~} z$0i<3(j~K!ox_3KszfuItR@$QhY-!kEquIo}rF@x%ZyA8a|SOa@M zUO9O94=Sz(QB6`?c<0Tl7xXne9ryorS1`w=3!;eYsAElJzy!juD8!h< zI(Ys$p2s!prspIDF`dW1_ZG1AqXG$KjNZG)WU8^c;H5ZvKPhATe-1;Fd2$A|DZ_-} zf-o?uuIJ6AhK2!W&DMjCZ}pCnrP0nHOaj#SqI@uR<9j2)T<6YdJWjSYVVHGPE^LcY z&O&_`fu>2q5;1d{8vK86caDy^3aY-GIV*;re77C8>toH!gXrB;YRpn-?DpOH>YOKN zG>^?hp+?-vd0cNsjgGnOMWCcU$8^T4$-yk7;#Jwm=DmO|uMQZ`bNJ1&Y0#8vmT=dP zx^d}kA2yL&0f!D(rGoY4e3LdJhM;?{KN0&xpInR4$gulvoyL%a*p3H@ z;I$5NphnMS(c_{s)4^QtygyWpHZ|?OAL!oBkX*0@3H}Dxt#@G_>;JWvj>theDP!rk zJ0>j^Z1&kVKBl>kYUcR51a5Pkj=9WVU$+AGyc+!xFmkwp)z5wqbpFNF z(=okEn>47Cx^>)g?et)dwXf{L{)0KVHF_xWOg~*$;Jo#rkW?b0RV4Hh;$D_xO@s^b zFrQcP*fTq@YwsvjS-|6W_TcoB6UPybs`W$y^`~E*9*o`Jm&1ckYz^jQady0qu5yg6 z4Ii#s*cW1qc@sxWk&QjX>@CC^Boqt**RA+FMv1+SRu#|uc0O*tdPdcYkHHdayXK;9 zy!gAbf-x*#yKcox$QGDV$`k#F5YjK=kMmcC^0D@XF9biLnuugv7DzVUrN_BLS>xy> zh#96ihVGP$Cm*;1vu3mfuXpXsg3Ke+s=A%ab9}Je_In=ZTm^?CIC{6S&cw5x#hH6kcRj(`a^A@{iPD zknc_e{7j3ADH)wSQM~VsRTtfU%#SK=UEpSAI9no$U295PqwJss+jIv=CKUmSl&e0Z~~yrvlU5T$AnZ;MQcK#d`=T4-S_wI8y$? zjdA?k$G&jaY{8f=2ev`SOAaDWBS~4paU8omq-s9i1xX3jUaE&EV_8qJ@ughR>gywh z1_)5GjwN@;4b9iYsjDVsD@;V0tGBo6NRuJQbcsAzAP=j$fJ5mu)2}>v^2k^PL)i*) x1q-$5RLDu*U;fp6gJszPJ3?0fm*u|#3;;}D@S$Y7#K-^u002ovPDHLkV1n%}8({zd literal 0 HcmV?d00001 diff --git a/core/res/res/drawable-hdpi/stat_sys_throttled.png b/core/res/res/drawable-hdpi/stat_sys_throttled.png new file mode 100644 index 0000000000000000000000000000000000000000..e43fbaee4caf048ceddb43d3620a536d73575753 GIT binary patch literal 2503 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 R_p1N^002ovPDHLkV1h^D%c1}P literal 0 HcmV?d00001 diff --git a/core/res/res/drawable-mdpi/stat_sys_throttle_warning.png b/core/res/res/drawable-mdpi/stat_sys_throttle_warning.png new file mode 100644 index 0000000000000000000000000000000000000000..368880328e2c88a782c718f900fb6577c0216017 GIT binary patch literal 1670 zcmV;126_33P)P000>X1^@s6#OZ}&0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU&C`m*?RCwBiR(Wg`RT%%hx%QaZqq{v_ ziwXs+#EKe~${`YwMoNq%;y(sMnh-S_6{E(e1QQPwFH97{BSbU`A_@^fQKAMBjR#nb z1!`)*VoSHjc6VpzdNY1AwQGS!Ci`XIeDA&Q_9t#*zQP($32R$p zmS220My&6-!gHOiVXZn7jn_gB$H15{j>9f&W0(y38%Ck8Q;|=43~e{ zk3p%>2TRX2gUN4&V>OB#PJ+9hA9*yLCt*&LjJlMF`YHk4{Vuw*9D9GW@l&T02$Cad zkV?(S_rBk{eE(I%Dtp8hKc0m{K{kq6jg)oBk$5H)Nzo2NQWJ3Rq8J+MB^)?Bg#8DO zp-?mtkA*RPT0NQ?66iec;DrzJ$PT-Kv8LYdL+B^P)E&=V7Q{%hZ3~7zM8S0LXQG^8 zk_wx=OT|SzwKNICa?rM74~`tugOnxQDR#qndq*cGWg>Xuo;i48X#x+t){ktC6A&6) zLvQf7b0^7vKJeuF2ab^DT*QNSLQ+&sQnbLursQQ%ZZh2WYA@9h0%RP!*hk4y_t}e=a=|ckvfORquAsS)VObWgY*cXJIUzi` zp#vqZLJU{J;a;#eN^}Q8D2;-x;?cFgq9LQ8`2r=_<5Yg!WmUG#X9n42+cpVsn;j|_ z%d&$**IpV$=U*lKd?*J`RLlHR$+juzQVbnOb+rFc#N26WneWRZxuZ%rS;fyM#=nPgKFMtusT02jxTpDW_+FVt z5y_Z@zCjbNCj~s8{L5{vH8{H}gr(Qlz^3=nJs;+QAuG6N`tTEvlRT1{_&2*;3L-Da zvJ>FW?ILmo2eqjX33keXw)GR(xJ|*$EsfZ;s{_TMVU7|>^kV0;j@OxJdGn$h0{$DHdt$k+0L9BXhU(f~QNF}^6`?D^tz@ekV zLFW+ZYzP8u&btD*1jZ@tobMfn62Kk_@Rr?2P*)ko+{-G;*gKvK7OsOBYY6`R_tCu> z7bP(1Y!#b#9S&gSzAS3A)JTI4EtE zlxiv@th#e5{yd(?dvwon1%zeK@8$!FjtWhK?Ny>4HOIZ5QN04dS zeEi}}%%5Ie4)R!9II|WzUz!!1Tk_zCFl_47jKCr}@c8Oq-kl%d$|#tlCx!j(S?t=^jp3q=ikO0y85vxDVH)53bQE{2+=gRW zfr>VT+DZ+Q$hs*GjX%D#AP78*&wLxAz>ukqA{v(F(R$~JK|{LEINHdupkAbNJT=E z+g#6E*OM)YJ^eJ(9fPtc56>Ea=ja3jr8Sf27X?!)s3FE7N@4OKg4%S{qUOEi>o;3B zd7gL5JfUZ};LjC~gPU)CW8Q5$x+hPFy_e9W#9zGv)r|yKOrAUyM^Y0N@laq~RVv&^ zC2-T{>lS=<+V~YOS$7h{_h>?`hPtX~Dj5qsOda9&ylzKwd5a9O{EWKSt?f81Yb|TmBrli0A-Ofd=R9V#(h9 z$;%6@|NB}QS4sH;fF%2#Rg%I~B8<371xcV&%mS|lawh)Vb;}vQyZ#Yi0RQKI6GC=5 Qp#T5?07*qoM6N<$f&~g6djJ3c literal 0 HcmV?d00001 diff --git a/core/res/res/drawable-mdpi/stat_sys_throttled.png b/core/res/res/drawable-mdpi/stat_sys_throttled.png new file mode 100644 index 0000000000000000000000000000000000000000..efb64ad7b849dcf66ed98c07c1055c9a75382433 GIT binary patch literal 1519 zcmVP000>X1^@s6#OZ}&0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%kx4{BRCwC7R%>ijRS^E}bKj5dww0z4 zsDg@$#)Q^1Du&3T(i-KJ@~-8dM#kECUPyw;-#hz7*$>GTCO_gX z@}~{~(UD2_IjOI)1i5?wz{vDs^1Uin&-P=|c!@ir?cAt!>uKyhbQyQuDx-CNpa4jq z@>!6_k-x?~h?YrFxl9kUg=UB@N}uXaz==d38eTt$o<0pNbHkWCQO#$f2FBpmPMK#H z4N}``J=}(8O!g4nAb;zoH=W=uD`vB4iomL2mgu*jK-YB=gCYu|JK@5zR}Y{omO;~; z2<{o1t3XYq2d_L+f@IpniVcTwv8xxFmhHFI!D#%LC6pKjdpLh4CbO!YmUNsCY@`hk z)DXIpZY+Aa1DCo}C=TUjcg0i1NNXmRZ#aOzdjtw7%=Zu4=31Qm4uCMqgxyJs{HCEm zP(p}lMOd=&6t2YwXYGZyGdSPv&HHwq@Qa|@GGk=&5j~hvS&C?A zaA0Rm8I6+{I{CSbXlFa!Xoon6VGxMLl8R#H3`Y1pTU(CR^GD~O|GJdIj0Yn${We;+ z{Xr2Ax057+8j2-(jI?yp>a;9659*K`Wno>E;gRHhqdSe_q41swg%t zA5TG>!|W`|&S0#QgNOp7w9I3sY4FJ(TGz(8qp_;K0%tp8n6t7Sm#?NdNS{%Pg|GdB zbe3UiWijd>D=DyJjxPcsBIOJ%XEGWTN!VjCE8|k&i6@PIPR^ogf)A(8UO|2HE+ljx zomy@fy>0>9TdU|_!q zG)_aO=?Of&_H*80N!{rpFRcFi;^Q}}F`*)e{okL*qV;>h#4weF=8h^M(^)980#jp~ zPk&ge=O&)gOQy1LE7(j85DQVCangeqJ?S9g1`(y`3cwJ}Aq=ZCx!3;85U*X-|gi#u>=!|rF{9iJS-!q(l$ z+5rk?5TM8}Bj6ErRmME>mn@)qP0yXf(f8^)6dBKz6}wnT*x@Rcr+Gt5psS}BYc}o0 z^BcZ_C51VFNrOlgKxvVRQBjvp_6x|*k%F61owS25w4WE}mD3yc)yR_gQFkm8`ll;} zc#>MuNKq!aTU3y}aLANe6Oo_`<)uE9MBG<(!mAE7dZM_M7^SAHRCI9C`Vkjf Tethering active Touch to configure + + + + Excessive data use warning + If your data use pattern continues you may be subject to bandwidth restrictions - touch for more information + + + + Bandwidth Restricted + Your mobile data bandwidth is being reduced because of excessive data use - touch for more information diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 25a60a6796ca8..9d5d035b083af 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -99,6 +99,7 @@ class ServerThread extends Thread { DockObserver dock = null; UiModeManagerService uiMode = null; RecognitionManagerService recognition = null; + ThrottleService throttle = null; // Critical services... try { @@ -268,6 +269,15 @@ class ServerThread extends Thread { Slog.e(TAG, "Failure starting Connectivity Service", e); } + try { + Slog.i(TAG, "Throttle Service"); + throttle = new ThrottleService(context); + ServiceManager.addService( + Context.THROTTLE_SERVICE, throttle); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting ThrottleService", e); + } + try { Slog.i(TAG, "Accessibility Manager"); ServiceManager.addService(Context.ACCESSIBILITY_SERVICE, @@ -457,6 +467,7 @@ class ServerThread extends Thread { final BatteryService batteryF = battery; final ConnectivityService connectivityF = connectivity; final DockObserver dockF = dock; + final ThrottleService throttleF = throttle; final UiModeManagerService uiModeF = uiMode; final AppWidgetService appWidgetF = appWidget; final WallpaperManagerService wallpaperF = wallpaper; @@ -488,6 +499,7 @@ class ServerThread extends Thread { if (wallpaperF != null) wallpaperF.systemReady(); if (immF != null) immF.systemReady(); if (locationF != null) locationF.systemReady(); + if (throttleF != null) throttleF.systemReady(); } }); diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java new file mode 100644 index 0000000000000..36931b23588cb --- /dev/null +++ b/services/java/com/android/server/ThrottleService.java @@ -0,0 +1,729 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.content.SharedPreferences; +import android.net.IThrottleManager; +import android.net.ThrottleManager; +import android.os.Binder; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.provider.Settings; +import android.util.Slog; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Random; + +// TODO - add comments - reference the ThrottleManager for public API +public class ThrottleService extends IThrottleManager.Stub { + + private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing"; + + private static final String TAG = "ThrottleService"; + private static boolean DBG = true; + private Handler mHandler; + private HandlerThread mThread; + + private Context mContext; + + private int mPolicyPollPeriodSec; + private static final int DEFAULT_POLLING_PERIOD_SEC = 60 * 10; + private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1; + + private static final int TESTING_RESET_PERIOD_SEC = 60 * 3; + + private static final int PERIOD_COUNT = 6; + + private long mPolicyThreshold; + // TODO - remove testing stuff? + private static final long DEFAULT_TESTING_THRESHOLD = 1 * 1024 * 1024; + private static final long DEFAULT_THRESHOLD = 0; // off by default + + private int mPolicyThrottleValue; + private static final int DEFAULT_THROTTLE_VALUE = 100; // 100 Kbps + + private int mPolicyResetDay; // 1-28 + + private long mLastRead; // read byte count from last poll + private long mLastWrite; // write byte count from last poll + + private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL"; + private static int POLL_REQUEST = 0; + private PendingIntent mPendingPollIntent; + private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET"; + private static int RESET_REQUEST = 1; + private PendingIntent mPendingResetIntent; + + private INetworkManagementService mNMService; + private AlarmManager mAlarmManager; + private NotificationManager mNotificationManager; + + private DataRecorder mRecorder; + + private int mThrottleLevel; // 0 for none, 1 for first throttle val, 2 for next, etc + + private String mPolicyIface; + + private static final int NOTIFICATION_WARNING = 2; + private static final int NOTIFICATION_ALL = 0xFFFFFFFF; + private int mPolicyNotificationsAllowedMask; + + private Notification mThrottlingNotification; + private boolean mWarningNotificationSent = false; + + public ThrottleService(Context context) { + if (DBG) Slog.d(TAG, "Starting ThrottleService"); + mContext = context; + + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + Intent pollIntent = new Intent(ACTION_POLL, null); + mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); + Intent resetIntent = new Intent(ACTION_RESET, null); + mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0); + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + mNMService = INetworkManagementService.Stub.asInterface(b); + + mNotificationManager = (NotificationManager)mContext.getSystemService( + Context.NOTIFICATION_SERVICE); + } + + private void enforceAccessPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, + "ThrottleService"); + } + + public synchronized long getResetTime(String iface) { + enforceAccessPermission(); + if (iface.equals(mPolicyIface) && (mRecorder != null)) mRecorder.getPeriodEnd(); + return 0; + } + public synchronized long getPeriodStartTime(String iface) { + enforceAccessPermission(); + if (iface.equals(mPolicyIface) && (mRecorder != null)) mRecorder.getPeriodStart(); + return 0; + } + //TODO - a better name? getCliffByteCountThreshold? + public synchronized long getCliffThreshold(String iface, int cliff) { + enforceAccessPermission(); + if ((cliff == 0) && iface.equals(mPolicyIface)) { + return mPolicyThreshold; + } + return 0; + } + // TODO - a better name? getThrottleRate? + public synchronized int getCliffLevel(String iface, int cliff) { + enforceAccessPermission(); + if ((cliff == 0) && iface.equals(mPolicyIface)) { + return mPolicyThrottleValue; + } + return 0; + } + + public synchronized long getByteCount(String iface, int dir, int period, int ago) { + enforceAccessPermission(); + if (iface.equals(mPolicyIface) && + (period == ThrottleManager.PERIOD_CYCLE) && + (mRecorder != null)) { + if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago); + if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago); + } + return 0; + } + + // TODO - a better name - getCurrentThrottleRate? + public synchronized int getThrottle(String iface) { + enforceAccessPermission(); + if (iface.equals(mPolicyIface) && (mThrottleLevel == 1)) { + return mPolicyThrottleValue; + } + return 0; + } + + void systemReady() { + if (DBG) Slog.d(TAG, "systemReady"); + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget(); + } + }, new IntentFilter(ACTION_POLL)); + + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget(); + } + }, new IntentFilter(ACTION_RESET)); + + // use a new thread as we don't want to stall the system for file writes + mThread = new HandlerThread(TAG); + mThread.start(); + mHandler = new MyHandler(mThread.getLooper()); + mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget(); + } + + + private static final int EVENT_REBOOT_RECOVERY = 0; + private static final int EVENT_POLICY_CHANGED = 1; + private static final int EVENT_POLL_ALARM = 2; + private static final int EVENT_RESET_ALARM = 3; + private class MyHandler extends Handler { + public MyHandler(Looper l) { + super(l); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_REBOOT_RECOVERY: + onRebootRecovery(); + break; + case EVENT_POLICY_CHANGED: + onPolicyChanged(); + break; + case EVENT_POLL_ALARM: + onPollAlarm(); + break; + case EVENT_RESET_ALARM: + onResetAlarm(); + } + } + + private void onRebootRecovery() { + if (DBG) Slog.d(TAG, "onRebootRecovery"); + // check for sim change TODO + // reregister for notification of policy change + + // register for roaming indication change + // check for roaming TODO + + mRecorder = new DataRecorder(mContext, ThrottleService.this); + + // get policy + mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget(); + + // evaluate current conditions + mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget(); + } + + private void onSimChange() { + // TODO + } + + // check for new policy info (threshold limit/value/etc) + private void onPolicyChanged() { + boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true"); + + int pollingPeriod = DEFAULT_POLLING_PERIOD_SEC; + if (testing) pollingPeriod = TESTING_POLLING_PERIOD_SEC; + mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod); + + // TODO - remove testing stuff? + long defaultThreshold = DEFAULT_THRESHOLD; + if (testing) defaultThreshold = DEFAULT_TESTING_THRESHOLD; + synchronized (ThrottleService.this) { + mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(), + Settings.Secure.THROTTLE_THRESHOLD, defaultThreshold); + mPolicyThrottleValue = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_VALUE, DEFAULT_THROTTLE_VALUE); + } + mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_RESET_DAY, -1); + if (mPolicyResetDay == -1 || + ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) { + Random g = new Random(); + mPolicyResetDay = 1 + g.nextInt(28); // 1-28 + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay); + } + synchronized (ThrottleService.this) { + mPolicyIface = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.THROTTLE_IFACE); + // TODO - read default from resource so it's device-specific + if (mPolicyIface == null) mPolicyIface = "rmnet0"; + } + + mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.THROTTLE_NOTIFICATION_TYPE, NOTIFICATION_ALL); + + Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" + mPolicyPollPeriodSec + + ", threshold=" + mPolicyThreshold + ", value=" + mPolicyThrottleValue + + ", resetDay=" + mPolicyResetDay + ", noteType=" + + mPolicyNotificationsAllowedMask); + + Calendar end = calculatePeriodEnd(); + Calendar start = calculatePeriodStart(end); + + mRecorder.setNextPeriod(start,end); + + mAlarmManager.cancel(mPendingResetIntent); + mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(), + mPendingResetIntent); + } + + private void onPollAlarm() { + long now = SystemClock.elapsedRealtime(); + long next = now + mPolicyPollPeriodSec*1000; + long incRead = 0; + long incWrite = 0; + try { + incRead = mNMService.getInterfaceRxCounter(mPolicyIface) - mLastRead; + incWrite = mNMService.getInterfaceTxCounter(mPolicyIface) - mLastWrite; + } catch (RemoteException e) { + Slog.e(TAG, "got remoteException in onPollAlarm:" + e); + } + + mRecorder.addData(incRead, incWrite); + + long periodRx = mRecorder.getPeriodRx(0); + long periodTx = mRecorder.getPeriodTx(0); + long total = periodRx + periodTx; + if (DBG) { + Slog.d(TAG, "onPollAlarm - now =" + now + ", read =" + incRead + + ", written =" + incWrite + ", new total =" + total); + } + mLastRead += incRead; + mLastWrite += incWrite; + + checkThrottleAndPostNotification(total); + + Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION); + broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx); + broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx); + broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, mRecorder.getPeriodStart()); + broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, mRecorder.getPeriodEnd()); + mContext.sendStickyBroadcast(broadcast); + + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, next, mPendingPollIntent); + } + + private void checkThrottleAndPostNotification(long currentTotal) { + // are we even doing this? + if (mPolicyThreshold == 0) + return; + + // check if we need to throttle + if (currentTotal > mPolicyThreshold) { + if (mThrottleLevel != 1) { + synchronized (ThrottleService.this) { + mThrottleLevel = 1; + } + if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!"); + try { + mNMService.setInterfaceThrottle(mPolicyIface, + mPolicyThrottleValue, mPolicyThrottleValue); + } catch (Exception e) { + Slog.e(TAG, "error setting Throttle: " + e); + } + + mNotificationManager.cancel(com.android.internal.R.drawable. + stat_sys_throttle_warning); + + postNotification(com.android.internal.R.string.throttled_notification_title, + com.android.internal.R.string.throttled_notification_message, + com.android.internal.R.drawable.stat_sys_throttled); + + Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION); + broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue); + mContext.sendStickyBroadcast(broadcast); + + } // else already up! + } else { + if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) { + // check if we should warn about throttle + if (currentTotal > (mPolicyThreshold/2) && !mWarningNotificationSent) { + mWarningNotificationSent = true; + mNotificationManager.cancel(com.android.internal.R.drawable. + stat_sys_throttle_warning); + postNotification(com.android.internal.R.string. + throttle_warning_notification_title, + com.android.internal.R.string. + throttle_warning_notification_message, + com.android.internal.R.drawable.stat_sys_throttle_warning); + } else { + mWarningNotificationSent =false; + } + } + } + } + + private void postNotification(int titleInt, int messageInt, int icon) { + Intent intent = new Intent(); + // TODO - fix up intent + intent.setClassName("com.android.settings", "com.android.settings.TetherSettings"); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + Resources r = Resources.getSystem(); + CharSequence title = r.getText(titleInt); + CharSequence message = r.getText(messageInt); + if (mThrottlingNotification == null) { + mThrottlingNotification = new Notification(); + mThrottlingNotification.when = 0; + // TODO - fixup icon + mThrottlingNotification.icon = icon; + mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND; +// mThrottlingNotification.flags = Notification.FLAG_ONGOING_EVENT; + } + mThrottlingNotification.tickerText = title; + mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi); + + mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification); + } + + + private synchronized void clearThrottleAndNotification() { + if (mThrottleLevel == 1) { + synchronized (ThrottleService.this) { + mThrottleLevel = 0; + } + try { + mNMService.setInterfaceThrottle(mPolicyIface, -1, -1); + } catch (Exception e) { + Slog.e(TAG, "error clearing Throttle: " + e); + } + Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION); + broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1); + mContext.sendStickyBroadcast(broadcast); + } + mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttle_warning); + mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttled); + mWarningNotificationSent = false; + } + + private Calendar calculatePeriodEnd() { + Calendar end = GregorianCalendar.getInstance(); + int day = end.get(Calendar.DAY_OF_MONTH); + end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay); + end.set(Calendar.HOUR_OF_DAY, 0); + end.set(Calendar.MINUTE, 0); + if (day >= mPolicyResetDay) { + int month = end.get(Calendar.MONTH); + if (month == Calendar.DECEMBER) { + end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1); + month = Calendar.JANUARY - 1; + } + end.set(Calendar.MONTH, month + 1); + } + + // TODO - remove! + if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { + end = GregorianCalendar.getInstance(); + end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC); + } + return end; + } + private Calendar calculatePeriodStart(Calendar end) { + Calendar start = (Calendar)end.clone(); + int month = end.get(Calendar.MONTH); + if (end.get(Calendar.MONTH) == Calendar.JANUARY) { + month = Calendar.DECEMBER + 1; + start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1); + } + start.set(Calendar.MONTH, month - 1); + + // TODO - remove!! + if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { + start = (Calendar)end.clone(); + start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC); + } + return start; + } + + private void onResetAlarm() { + if (DBG) { + Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) + + " bytes read and " + mRecorder.getPeriodTx(0) + " written"); + } + + Calendar end = calculatePeriodEnd(); + Calendar start = calculatePeriodStart(end); + + clearThrottleAndNotification(); + + mRecorder.setNextPeriod(start,end); + + mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(), + mPendingResetIntent); + } + } + + // records bytecount data for a given time and accumulates it into larger time windows + // for logging and other purposes + // + // since time can be changed (user or network action) we will have to track the time of the + // last recording and deal with it. + private static class DataRecorder { + long[] mPeriodRxData; + long[] mPeriodTxData; + int mCurrentPeriod; + int mPeriodCount; + + Calendar mPeriodStart; + Calendar mPeriodEnd; + + ThrottleService mParent; + Context mContext; + SharedPreferences mSharedPreferences; + + DataRecorder(Context context, ThrottleService parent) { + mContext = context; + mParent = parent; + + synchronized (mParent) { + mPeriodCount = 6; + mPeriodRxData = new long[mPeriodCount]; + mPeriodTxData = new long[mPeriodCount]; + + mPeriodStart = Calendar.getInstance(); + mPeriodEnd = Calendar.getInstance(); + + mSharedPreferences = mContext.getSharedPreferences("ThrottleData", + android.content.Context.MODE_PRIVATE); + + zeroData(0); + retrieve(); + } + } + + void setNextPeriod(Calendar start, Calendar end) { + if (DBG) { + Slog.d(TAG, "setting next period to " + start.getTimeInMillis() + + " --until-- " + end.getTimeInMillis()); + } + // if we roll back in time to a previous period, toss out the current data + // if we roll forward to the next period, advance to the next + + if (end.before(mPeriodStart)) { + if (DBG) { + Slog.d(TAG, " old start was " + mPeriodStart.getTimeInMillis() + ", wiping"); + } + synchronized (mParent) { + mPeriodRxData[mCurrentPeriod] = 0; + mPeriodTxData[mCurrentPeriod] = 0; + } + } else if(start.after(mPeriodEnd)) { + if (DBG) { + Slog.d(TAG, " old end was " + mPeriodEnd.getTimeInMillis() + ", following"); + } + synchronized (mParent) { + ++mCurrentPeriod; + if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0; + mPeriodRxData[mCurrentPeriod] = 0; + mPeriodTxData[mCurrentPeriod] = 0; + } + } else { + if (DBG) Slog.d(TAG, " we fit - ammending to last period"); + } + setPeriodStart(start); + setPeriodEnd(end); + record(); + } + + public long getPeriodEnd() { + synchronized (mParent) { + return mPeriodEnd.getTimeInMillis(); + } + } + + private void setPeriodEnd(Calendar end) { + synchronized (mParent) { + mPeriodEnd = end; + } + } + + public long getPeriodStart() { + synchronized (mParent) { + return mPeriodStart.getTimeInMillis(); + } + } + + private void setPeriodStart(Calendar start) { + synchronized (mParent) { + mPeriodStart = start; + } + } + + public int getPeriodCount() { + synchronized (mParent) { + return mPeriodCount; + } + } + + private void zeroData(int field) { + synchronized (mParent) { + for(int period = 0; period