1 package com.aelitis.azureus.core.speedmanager.impl.v2;
2 
3 import org.gudy.azureus2.core3.config.COConfigurationManager;
4 import org.gudy.azureus2.core3.util.SystemTime;
5 import org.gudy.azureus2.core3.util.RealTimeInfo;
6 import com.aelitis.azureus.core.speedmanager.SpeedManagerLimitEstimate;
7 import com.aelitis.azureus.core.speedmanager.SpeedManagerPingMapper;
8 import com.aelitis.azureus.core.speedmanager.SpeedManager;
9 import com.aelitis.azureus.core.speedmanager.impl.SpeedManagerAlgorithmProviderAdapter;
10 import com.aelitis.azureus.core.AzureusCoreFactory;
11 
12 /**
13  * Created on May 23, 2007
14  * Created by Alan Snyder
15  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
16  * <p/>
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License
19  * as published by the Free Software Foundation; either version 2
20  * of the License, or (at your option) any later version.
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24  * GNU General Public License for more details.
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28  */
29 
30 /**
31  * This class is responsible for re-adjusting the limits used by AutoSpeedV2.
32  *
33  * This class will keep track of the "status" (i.e. seeding, downloading)of the
34  * application. It will then re-adjust the MAX limits when it thinks limits
35  * are being reached.
36  *
37  * Here are the rules it will use.
38  *
39  * #1) When seeding. If the upload is AT_LIMIT for a period of time it will allow
40  * that to adjust upward.
41  * #2) When downloading. If the download is AT_LIMIT for a period of time it will
42  * allow that to adjust upward.
43  *
44  * #3) When downloading, if a down-tick is detected and the upload is near a limit,
45  * it will drop the upload limit to 80% of MAX_UPLOAD.
46  *
47  * #4) Once that limit is reached it will drop both the upload and download limits together.
48  *
49  * #5) Seeding mode is triggered when - download bandwidth at LOW - compared to CAPACITY for 5 minutes continously.
50  *
51  * #6) Download mode is triggered when - download bandwidth reaches MEDIUM - compared to CURRENT_LIMIT for the first time.
52  *
53  * Rules #5 and #6 favor downloading over seeding.
54  *
55  */
56 
57 public class SpeedLimitMonitor implements PSMonitorListener
58 {
59 
60     //use for home network.
61     private int uploadLimitMax = SMConst.START_UPLOAD_RATE_MAX;
62     private int uploadLimitMin = SMConst.calculateMinUpload( uploadLimitMax );
63     private int downloadLimitMax = SMConst.START_DOWNLOAD_RATE_MAX;
64     private int downloadLimitMin = SMConst.calculateMinDownload( downloadLimitMax );
65 
66     private TransferMode transferMode = new TransferMode();
67 
68     //Upload and Download bandwidth usage modes. Compare usage to current limit.
69     private SaturatedMode uploadBandwidthStatus =SaturatedMode.NONE;
70     private SaturatedMode downloadBandwidthStatus =SaturatedMode.NONE;
71 
72     //Compare current limit to max limit.
73     private SaturatedMode uploadLimitSettingStatus=SaturatedMode.AT_LIMIT;
74     private SaturatedMode downloadLimitSettingStatus=SaturatedMode.AT_LIMIT;
75 
76     //How much confidence to we have in the current limits?
77     private SpeedLimitConfidence uploadLimitConf = SpeedLimitConfidence.NONE;
78     private SpeedLimitConfidence downloadLimitConf = SpeedLimitConfidence.NONE;
79 
80     private long clLastIncreaseTime =-1;
81     private long clFirstBadPingTime=-1;
82 
83     private boolean currTestDone;
84     private boolean beginLimitTest;
85     private int highestUploadRate=0;
86     private int highestDownloadRate=0;
87     private int preTestUploadCapacity=5042;
88     private int preTestUploadLimit=5142;
89     private int preTestDownloadCapacity=5042;
90     private int preTestDownloadLimit=5142;
91 
92     public static final String UPLOAD_CONF_LIMIT_SETTING="SpeedLimitMonitor.setting.upload.limit.conf";
93     public static final String DOWNLOAD_CONF_LIMIT_SETTING="SpeedLimitMonitor.setting.download.limit.conf";
94     public static final String UPLOAD_CHOKE_PING_COUNT="SpeedLimitMonitor.setting.choke.ping.count";
95     private static final long CONF_LIMIT_TEST_LENGTH=1000*30;
96 
97     //these methods are used to see how high limits can go.
98     private boolean isUploadMaxPinned=true;
99     private boolean isDownloadMaxPinned=true;
100     private long uploadAtLimitStartTime =SystemTime.getCurrentTime();
101     private long downloadAtLimitStartTime = SystemTime.getCurrentTime();
102     private int uploadChokePingCount = 1;
103     private int uploadPinCounter = 0;
104 
105     private static final long TIME_AT_LIMIT_BEFORE_UNPINNING = 30 * 1000; //30 seconds.
106 
107     //which percent of the measured upload capacity to use in download and seeding mode.
108     public static final String USED_UPLOAD_CAPACITY_DOWNLOAD_MODE = "SpeedLimitMonitor.setting.upload.used.download.mode";
109     public static final String USED_UPLOAD_CAPACITY_SEEDING_MODE = "SpeedLimitMonitor.setting.upload.used.seeding.mode";
110     private float percentUploadCapacityDownloadMode = 0.6f;
111 
112     //PingSpaceMaps for the entire session.
113     PingSpaceMapper pingMapOfDownloadMode;
114     PingSpaceMapper pingMapOfSeedingMode;
115 
116 
117     boolean useVariancePingMap = false;
118     SpeedManagerPingMapper transientPingMap;
119 
120     PingSpaceMon longTermMonitor = new PingSpaceMon();
121 
122     LimitControl slider = new LimitControlDropUploadFirst();
123 
124     SpeedLimitListener persistentMapListener;
125 
SpeedLimitMonitor(SpeedManager sm)126     public SpeedLimitMonitor(SpeedManager sm){
127 
128         //
129         longTermMonitor.addListener( this );
130 
131         persistentMapListener = new SpeedLimitListener( this );
132 
133         sm.addListener( persistentMapListener );
134     }
135 
136 
137     /**
138      * Splitting the limits our from other setting for SpeedManagerAlgorithmTI.
139      */
updateSettingsFromCOConfigManager()140     public void updateSettingsFromCOConfigManager(){
141         percentUploadCapacityDownloadMode = (float)
142                 COConfigurationManager.getIntParameter(SpeedLimitMonitor.USED_UPLOAD_CAPACITY_DOWNLOAD_MODE, 60)/100.0f;
143 
144         slider.updateSeedSettings(percentUploadCapacityDownloadMode);
145     }
146 
updateFromCOConfigManager()147     public void updateFromCOConfigManager(){
148 
149         uploadLimitMax = COConfigurationManager.getIntParameter(SpeedManagerAlgorithmProviderV2.SETTING_UPLOAD_MAX_LIMIT);
150         uploadLimitMin = SMConst.calculateMinUpload( uploadLimitMax );
151 
152         downloadLimitMax =COConfigurationManager.getIntParameter(SpeedManagerAlgorithmProviderV2.SETTING_DOWNLOAD_MAX_LIMIT);
153         downloadLimitMin=SMConst.calculateMinDownload( downloadLimitMax );
154 
155         uploadLimitConf = SpeedLimitConfidence.parseString(
156                 COConfigurationManager.getStringParameter( SpeedLimitMonitor.UPLOAD_CONF_LIMIT_SETTING ));
157         downloadLimitConf = SpeedLimitConfidence.parseString(
158                 COConfigurationManager.getStringParameter( SpeedLimitMonitor.DOWNLOAD_CONF_LIMIT_SETTING));
159 
160         percentUploadCapacityDownloadMode = (float)
161                 COConfigurationManager.getIntParameter(SpeedLimitMonitor.USED_UPLOAD_CAPACITY_DOWNLOAD_MODE, 60)/100.0f;
162 
163         uploadChokePingCount = Math.min(
164                     COConfigurationManager.getIntParameter(SpeedLimitMonitor.UPLOAD_CHOKE_PING_COUNT),
165                     30 );
166 
167         slider.updateLimits(uploadLimitMax,uploadLimitMin,downloadLimitMax,downloadLimitMin);
168         slider.updateSeedSettings(percentUploadCapacityDownloadMode);
169 
170 
171         if( isSettingDownloadUnlimited() ){
172             slider.setDownloadUnlimitedMode( true );
173         }
174 
175     }//updateFromCOConfigManager
176 
177     /**
178      * replaces - updateFromCOConfigManager()
179      */
readFromPersistentMap()180     public void readFromPersistentMap(){
181         //get persistent mapper.
182         SpeedManager sm = AzureusCoreFactory.getSingleton().getSpeedManager();
183 
184         //get upload estimate.
185         SpeedManagerLimitEstimate uEst = SMConst.filterEstimate(
186                                             sm.getEstimatedUploadCapacityBytesPerSec(),
187                                             SMConst.START_UPLOAD_RATE_MAX );
188 
189         int upPingMapLimit = uEst.getBytesPerSec();
190         if(upPingMapLimit<SMConst.START_UPLOAD_RATE_MAX){
191             //will find upload limit via slow search.
192             uploadLimitMax = SMConst.START_UPLOAD_RATE_MAX;
193         }else{
194             uploadLimitMax = upPingMapLimit;
195         }
196         uploadLimitMin = SMConst.calculateMinUpload( uploadLimitMax );
197 
198 
199         //get download estimate.
200         SpeedManagerLimitEstimate dEst = SMConst.filterEstimate(
201                                             sm.getEstimatedDownloadCapacityBytesPerSec(),
202                                             SMConst.START_DOWNLOAD_RATE_MAX );
203 
204 
205         int downPingMapLimit = dEst.getBytesPerSec();
206         if( isSettingDownloadUnlimited() ){
207             slider.setDownloadUnlimitedMode(true);
208         }else{
209             slider.setDownloadUnlimitedMode(false);
210         }
211 
212         if(downPingMapLimit<SMConst.START_DOWNLOAD_RATE_MAX){
213             downloadLimitMax = SMConst.START_DOWNLOAD_RATE_MAX;
214         }else{
215             downloadLimitMax = downPingMapLimit;
216         }
217         downloadLimitMin = SMConst.calculateMinDownload( downloadLimitMax );
218 
219         uploadLimitConf = SpeedLimitConfidence.convertType( uEst.getEstimateType() );
220         downloadLimitConf = SpeedLimitConfidence.convertType( dEst.getEstimateType() );
221 
222         percentUploadCapacityDownloadMode = (float)
223                     COConfigurationManager.getIntParameter(SpeedLimitMonitor.USED_UPLOAD_CAPACITY_DOWNLOAD_MODE, 60)/100.0f;
224 
225         saveToCOConfiguration();
226     }
227 
228 
saveToCOConfiguration()229     public void saveToCOConfiguration(){
230         COConfigurationManager.setParameter(SpeedManagerAlgorithmProviderV2.SETTING_UPLOAD_MAX_LIMIT,uploadLimitMax);
231         COConfigurationManager.setParameter(SpeedManagerAlgorithmProviderV2.SETTING_DOWNLOAD_MAX_LIMIT,downloadLimitMax);
232         COConfigurationManager.setParameter(SpeedLimitMonitor.UPLOAD_CONF_LIMIT_SETTING, uploadLimitConf.getString() );
233         COConfigurationManager.setParameter(SpeedLimitMonitor.DOWNLOAD_CONF_LIMIT_SETTING, downloadLimitConf.getString() );
234         COConfigurationManager.setParameter(SpeedLimitMonitor.UPLOAD_CHOKE_PING_COUNT,uploadChokePingCount);
235     }
236 
logPMData(int oRate, SpeedLimitConfidence oConf, int nRate, float nConf, String type)237     private void logPMData(int oRate, SpeedLimitConfidence oConf, int nRate, float nConf, String type){
238 
239 //        SpeedManagerLogger.log("speed-limit-conf: "+type+" rate="+oRate+" conf="+oConf.getString()+"("+oConf.asEstimateType()
240 //                +") pm-rate="+nRate+" pm-conf="+nConf);
241 
242     }//logPMData
243 
logPMDataEx()244     public void logPMDataEx(){
245 
246         int tuploadLimitMax = COConfigurationManager.getIntParameter(SpeedManagerAlgorithmProviderV2.SETTING_UPLOAD_MAX_LIMIT);
247         int tdownloadLimitMax =COConfigurationManager.getIntParameter(SpeedManagerAlgorithmProviderV2.SETTING_DOWNLOAD_MAX_LIMIT);
248 
249         //for testing.
250         SpeedManager sm = AzureusCoreFactory.getSingleton().getSpeedManager();
251         SpeedManagerLimitEstimate dEst = sm.getEstimatedDownloadCapacityBytesPerSec();
252 
253         int tmpDMax = dEst.getBytesPerSec();
254         float tmpDMaxConf = dEst.getEstimateType();
255 
256 
257         // for testing.
258         SpeedManagerLimitEstimate uEst = sm.getEstimatedUploadCapacityBytesPerSec();
259         int tmpUMax = uEst.getBytesPerSec();
260         float tmpUMaxConf = uEst.getEstimateType();
261 
262         SpeedLimitConfidence tuploadLimitConf = SpeedLimitConfidence.parseString(
263                 COConfigurationManager.getStringParameter( SpeedLimitMonitor.UPLOAD_CONF_LIMIT_SETTING ));
264         SpeedLimitConfidence tdownloadLimitConf = SpeedLimitConfidence.parseString(
265                 COConfigurationManager.getStringParameter( SpeedLimitMonitor.DOWNLOAD_CONF_LIMIT_SETTING));
266 
267         //
268         logPMData(tuploadLimitMax, tuploadLimitConf, tmpUMax, tmpUMaxConf, "check-upload" );
269         logPMData(tdownloadLimitMax, tdownloadLimitConf, tmpDMax, tmpDMaxConf, "check-download" );
270 
271     }//logPMDataEx
272 
273 
274     /**
275      * The criteria for download being unlimited is if the ConfigPanel has the
276      * "download == 0 " && "type==fixed"
277      * @return - true
278      */
isSettingDownloadUnlimited()279     private boolean isSettingDownloadUnlimited(){
280 
281         SpeedManagerAlgorithmProviderAdapter adpter = SMInstance.getInstance().getAdapter();
282 
283         SpeedManager sm = adpter.getSpeedManager();
284         SpeedManagerLimitEstimate dEst = sm.getEstimatedDownloadCapacityBytesPerSec();
285 
286         int rate = dEst.getBytesPerSec();
287         float type = dEst.getEstimateType();
288 
289         //user or plug-in want the download rate unlimited.
290         if( rate==0 && type==SpeedManagerLimitEstimate.TYPE_MANUAL ){
291             return true;
292         }
293 
294         //start the search in unlimited mode.
295         if( rate==0 && type==SpeedManagerLimitEstimate.TYPE_UNKNOWN){
296             return true;
297         }
298 
299         return false;
300     }
301 
302     //SpeedLimitMonitorStatus
setDownloadBandwidthMode(int rate, int limit)303     public void setDownloadBandwidthMode(int rate, int limit){
304         downloadBandwidthStatus = SaturatedMode.getSaturatedMode(rate,limit);
305     }
306 
setUploadBandwidthMode(int rate, int limit)307     public void setUploadBandwidthMode(int rate, int limit){
308         uploadBandwidthStatus = SaturatedMode.getSaturatedMode(rate,limit);
309     }
310 
setDownloadLimitSettingMode(int currLimit)311     public void setDownloadLimitSettingMode(int currLimit){
312         downloadLimitSettingStatus = SaturatedMode.getSaturatedMode(currLimit, downloadLimitMax);
313     }
314 
setUploadLimitSettingMode(int currLimit)315     public void setUploadLimitSettingMode(int currLimit){
316         if( !transferMode.isDownloadMode() ){
317             uploadLimitSettingStatus = SaturatedMode.getSaturatedMode(currLimit, uploadLimitMax);
318         }else{
319             uploadLimitSettingStatus = SaturatedMode.getSaturatedMode(currLimit, uploadLimitMax);
320         }
321     }
322 
getUploadMaxLimit()323     public int getUploadMaxLimit(){
324         return uploadLimitMax;
325     }
326 
getDownloadMaxLimit()327     public int getDownloadMaxLimit(){
328         return downloadLimitMax;
329     }
330 
getUploadMinLimit()331     public int getUploadMinLimit(){
332         return uploadLimitMin;
333     }
334 
getDownloadMinLimit()335     public int getDownloadMinLimit(){
336         return downloadLimitMin;
337     }
338 
getUploadConfidence()339     public String getUploadConfidence(){
340         return uploadLimitConf.getString();
341     }
342 
getDownloadConfidence()343     public String getDownloadConfidence(){
344         return downloadLimitConf.getString();
345     }
346 
getDownloadBandwidthMode()347     public SaturatedMode getDownloadBandwidthMode(){
348         return downloadBandwidthStatus;
349     }
350 
getUploadBandwidthMode()351     public SaturatedMode getUploadBandwidthMode(){
352         return uploadBandwidthStatus;
353     }
354 
getDownloadLimitSettingMode()355     public SaturatedMode getDownloadLimitSettingMode(){
356         return downloadLimitSettingStatus;
357     }
358 
getUploadLimitSettingMode()359     public SaturatedMode getUploadLimitSettingMode(){
360         return uploadLimitSettingStatus;
361     }
362 
updateTransferMode()363     public void updateTransferMode(){
364 
365         transferMode.updateStatus( downloadBandwidthStatus );
366     }
367 
getTransferModeAsString()368     public String getTransferModeAsString(){
369         return transferMode.getString();
370     }
371 
getTransferMode()372     public TransferMode getTransferMode(){
373         return transferMode;
374     }
375 
376 
377     /**
378      * Are both the upload and download bandwidths usages is low?
379      * Otherwise false.
380      * @return -
381      */
bandwidthUsageLow()382     public boolean bandwidthUsageLow(){
383 
384         if( uploadBandwidthStatus.compareTo(SaturatedMode.LOW)<=0 &&
385                 downloadBandwidthStatus.compareTo(SaturatedMode.LOW)<=0){
386 
387             return true;
388 
389         }
390 
391         //Either upload or download is at MEDIUM or above.
392         return false;
393     }
394 
395     /**
396      *
397      * @return -
398      */
bandwidthUsageMedium()399     public boolean bandwidthUsageMedium(){
400         if( uploadBandwidthStatus.compareTo(SaturatedMode.MED)<=0 &&
401                 downloadBandwidthStatus.compareTo(SaturatedMode.MED)<=0){
402             return true;
403         }
404 
405         //Either upload or download is at MEDIUM or above.
406         return false;
407     }
408 
409     /**
410      * True if both are at limits.
411      * @return - true only if both the upload and download usages are at the limits.
412      */
bandwidthUsageAtLimit()413     public boolean bandwidthUsageAtLimit(){
414         if( uploadBandwidthStatus.compareTo(SaturatedMode.AT_LIMIT)==0 &&
415                 downloadBandwidthStatus.compareTo(SaturatedMode.AT_LIMIT)==0){
416             return true;
417         }
418         return false;
419     }
420 
421     /**
422      * True if the upload bandwidth usage is HIGH or AT_LIMIT.
423      * @return -
424      */
isUploadBandwidthUsageHigh()425     public boolean isUploadBandwidthUsageHigh(){
426         if( uploadBandwidthStatus.compareTo(SaturatedMode.AT_LIMIT)==0 ||
427                 uploadBandwidthStatus.compareTo(SaturatedMode.HIGH)==0){
428             return true;
429         }
430         return false;
431     }
432 
isEitherLimitUnpinned()433     public boolean isEitherLimitUnpinned(){
434         return ( !isUploadMaxPinned || !isDownloadMaxPinned );
435     }
436 
437     /**
438      * Does the same as createNewLimit except it drops the upload rate first when in download mode.
439      * @param signalStrength -
440      * @param multiple -
441      * @param currUpLimit -
442      * @param currDownLimit -
443      * @return  -
444      */
modifyLimits(float signalStrength, float multiple, int currUpLimit, int currDownLimit)445     public SMUpdate modifyLimits(float signalStrength, float multiple, int currUpLimit, int currDownLimit){
446 
447         //this flag is set in a previous method.
448         if( isStartLimitTestFlagSet() ){
449             SpeedManagerLogger.trace("modifyLimits - startLimitTesting.");
450             SMUpdate update = startLimitTesting(currUpLimit, currDownLimit);
451             return checkActiveProgressiveDownloadLimit( update );
452         }
453 
454 
455         if( isEitherLimitUnpinned() ){
456             SpeedManagerLogger.trace("modifyLimits - calculateNewUnpinnedLimits");
457             SMUpdate update = calculateNewUnpinnedLimits(signalStrength);
458             return checkActiveProgressiveDownloadLimit( update );
459         }
460 
461         slider.updateLimits(uploadLimitMax,uploadLimitMin,
462                 downloadLimitMax,downloadLimitMin);
463 
464         slider.updateStatus(currUpLimit,uploadBandwidthStatus,
465                 currDownLimit, downloadBandwidthStatus,transferMode);
466 
467         SMUpdate update = slider.adjust( signalStrength*multiple );
468         return checkActiveProgressiveDownloadLimit( update );
469     }//modifyLimits
470 
471     /**
472      * If a progressive download is currently active. Then the download limit should
473      * not be allowed to go below that limit, regardless of anything else.
474      * @param update -
475      * @return -
476      */
checkActiveProgressiveDownloadLimit( SMUpdate update )477     private SMUpdate checkActiveProgressiveDownloadLimit( SMUpdate update ){
478 
479         //Do we have an active download limit?
480         long prgDownLimit = RealTimeInfo.getProgressiveActiveBytesPerSec();
481 
482         //If the value is zero, then the no progressive download is currently active.
483         if( prgDownLimit==0 ){
484             return update;
485         }
486 
487         //We seem to have an active progressive download. Make sure the limit does not
488         //drop below that limit.
489         final int MULTIPLE = 2;
490         if( prgDownLimit*MULTIPLE > update.newDownloadLimit && update.newDownloadLimit!=0 )
491         {
492             log( "Active Progressive download in progress. Overriding limit. curr="+update.newDownloadLimit
493                     +" progDownloadLimit="+prgDownLimit*MULTIPLE );
494 
495             update.newDownloadLimit = (int)prgDownLimit*MULTIPLE;
496         }//if
497 
498         return update;
499     }//checkActiveProgressiveDownloadLimit
500 
501     /**
502      * Log debug info needed during beta period.
503      */
logPinningInfo()504     private void logPinningInfo() {
505         StringBuffer sb = new StringBuffer("pin: ");
506         if(isUploadMaxPinned){
507             sb.append("ul-pinned:");
508         }else{
509             sb.append("ul-unpinned:");
510         }
511         if(isDownloadMaxPinned){
512             sb.append("dl-pinned:");
513         }else{
514             sb.append("dl-unpinned:");
515         }
516         long currTime = SystemTime.getCurrentTime();
517         long upWait = currTime - uploadAtLimitStartTime;
518         long downWait = currTime - downloadAtLimitStartTime;
519         sb.append(upWait).append(":").append(downWait);
520         log( sb.toString() );
521     }
522 
523     /**
524      *
525      * @param signalStrength -
526      * @return -
527      */
calculateNewUnpinnedLimits(float signalStrength)528     public SMUpdate calculateNewUnpinnedLimits(float signalStrength){
529 
530         //first verify that is this is an up signal.
531         if(signalStrength<0.0f){
532             //down-tick is a signal to stop moving the files up.
533             isUploadMaxPinned=true;
534             isDownloadMaxPinned=true;
535         }//if
536 
537         //just verify settings to make sure everything is sane before updating.
538         boolean updateUpload=false;
539         boolean updateDownload=false;
540 
541         if( uploadBandwidthStatus.compareTo(SaturatedMode.AT_LIMIT)==0 &&
542                 uploadLimitSettingStatus.compareTo(SaturatedMode.AT_LIMIT)==0 ){
543             updateUpload=true;
544         }
545 
546         if( downloadBandwidthStatus.compareTo(SaturatedMode.AT_LIMIT)==0 &&
547                 downloadLimitSettingStatus.compareTo(SaturatedMode.AT_LIMIT)==0 ){
548             updateDownload=true;
549         }
550 
551         boolean uploadChanged=false;
552         boolean downloadChanged=false;
553 
554 
555         if(updateUpload && !transferMode.isDownloadMode() ){
556             //slow the upload rate the more.
557             uploadPinCounter++;
558             if( uploadPinCounter%(Math.ceil(Math.sqrt(uploadChokePingCount)))==0 ){
559                 //increase limit by calculated amount, but only if not in downloading mode.
560                 uploadLimitMax += calculateUnpinnedStepSize(uploadLimitMax);
561                 uploadChanged=true;
562                 COConfigurationManager.setParameter(
563                         SpeedManagerAlgorithmProviderV2.SETTING_UPLOAD_MAX_LIMIT, uploadLimitMax);
564                 COConfigurationManager.setParameter(
565                         SpeedLimitMonitor.UPLOAD_CHOKE_PING_COUNT,uploadChokePingCount);
566             }//if
567         }
568         if(updateDownload && !slider.isDownloadUnlimitedMode() ){
569             //increase limit by calculated amount.
570             downloadLimitMax += calculateUnpinnedStepSize(downloadLimitMax);
571             downloadChanged=true;
572             COConfigurationManager.setParameter(
573                     SpeedManagerAlgorithmProviderV2.SETTING_DOWNLOAD_MAX_LIMIT, downloadLimitMax);
574         }
575 
576         //apply any rules that need applied.
577         //The download limit can never be less then the upload limit. (Unless zero. UNLIMITED)
578         if( uploadLimitMax > downloadLimitMax){
579             downloadLimitMax = uploadLimitMax;
580             downloadChanged=true;
581             COConfigurationManager.setParameter(
582                     SpeedManagerAlgorithmProviderV2.SETTING_DOWNLOAD_MAX_LIMIT, downloadLimitMax);
583         }
584 
585         uploadLimitMin = SMConst.calculateMinUpload( uploadLimitMax );
586         downloadLimitMin = SMConst.calculateMinDownload( downloadLimitMax );
587 
588         if( slider.isDownloadUnlimitedMode() ){
589             SpeedManagerLogger.trace("upload unpinned while download is unlimited.");
590             return new SMUpdate(uploadLimitMax,uploadChanged, 0,false);
591         }
592 
593         return new SMUpdate(uploadLimitMax,uploadChanged, downloadLimitMax,downloadChanged);
594     }//calculateNewUnpinnedLimits
595 
596     /**
597      * If setting is less then 100kBytes take 1 kByte steps.
598      * If setting is less then 500kBytes take 5 kByte steps.
599      * if setting is larger take 10 kBytes steps.
600      * @param currLimitMax - current limit setting.
601      * @return - set size for next change.
602      */
calculateUnpinnedStepSize(int currLimitMax)603     private int calculateUnpinnedStepSize(int currLimitMax){
604         if(currLimitMax<102400){
605             return 1024;
606         }else if(currLimitMax<409600){
607             return 1024*5;
608         }else{
609             return 1024*10;
610         }
611     }//
612 
613     /**
614      * Make a decision about unpinning either the upload or download limit. This is based on the
615      * time we are saturating the limit without a down-tick signal.
616      */
checkForUnpinningCondition()617     public void checkForUnpinningCondition(){
618 
619         long currTime = SystemTime.getCurrentTime();
620 
621         //verify the download is not unlimited.
622         slider.setDownloadUnlimitedMode( isSettingDownloadUnlimited() );
623 
624         //upload useage must be at limits for a set period of time before unpinning.
625         if( !uploadBandwidthStatus.equals(SaturatedMode.AT_LIMIT) ||
626                 !uploadLimitSettingStatus.equals(SaturatedMode.AT_LIMIT) )
627         {
628             //start the clock over.
629             uploadAtLimitStartTime = currTime;
630         }else{
631             //check to see if we have been here for the time limit.
632             if( uploadAtLimitStartTime+(TIME_AT_LIMIT_BEFORE_UNPINNING* uploadChokePingCount) < currTime ){
633 
634                 if( isUploadConfidenceLow() ){
635                     if( !transferMode.isDownloadMode() ){
636                         //alway slow search the upload limit.
637                         isUploadMaxPinned = false;
638                     }
639                 }else{
640                     //Don't unpin the limit is we have absolute confidence in it.
641                     if( !isUploadConfidenceAbsolute() ){
642                         //we have been AT_LIMIT long enough. Time to un-pin the limit see if we can go higher.
643                         isUploadMaxPinned = false;
644                         SpeedManagerLogger.trace("unpinning the upload max limit!! #choke-pings="+uploadChokePingCount+
645                             ", pin-counter="+uploadPinCounter);
646                     }
647                 }
648             }
649         }
650 
651         //download usage must be at limits for a set period of time before unpinning.
652         if( !downloadBandwidthStatus.equals(SaturatedMode.AT_LIMIT) ||
653                 !downloadLimitSettingStatus.equals(SaturatedMode.AT_LIMIT) )
654         {
655             //start the clock over.
656             downloadAtLimitStartTime = currTime;
657         }else{
658             //check to see if we have been here for the time limit.
659             if( downloadAtLimitStartTime+TIME_AT_LIMIT_BEFORE_UNPINNING < currTime ){
660 
661                 if( isDownloadConfidenceLow() ){
662                     if( transferMode.isDownloadMode() ){
663                         triggerLimitTestingFlag();
664                     }
665                 }else{
666                     if( !isDownloadConfidenceAbsolute() ){
667                         //we have been AT_LIMIT long enough. Time to un-pin the limit see if we can go higher.
668                         isDownloadMaxPinned = false;
669                         SpeedManagerLogger.trace("unpinning the download max limit!!");
670                     }
671                 }
672             }
673         }
674 
675         logPinningInfo();
676     }
677 
678     /**
679      * If we have a down-tick signal then resetTimer all the counters for increasing the limits.
680      */
notifyOfDownSignal()681     public void notifyOfDownSignal(){
682 
683         if( !isUploadMaxPinned ){
684             uploadChokePingCount++;
685             String msg = "pinning the upload max limit, due to downtick signal. #downtick="+ uploadChokePingCount;
686             SpeedManagerLogger.trace(msg);
687             SMSearchLogger.log(msg);
688         }
689 
690         if( !isDownloadMaxPinned ){
691             String msg = "pinning the download max limit, due to downtick signal.";
692             SpeedManagerLogger.trace(msg);
693             SMSearchLogger.log(msg);
694         }
695 
696         resetPinSearch();
697     }
698 
resetPinSearch()699     void resetPinSearch(){
700         long currTime = SystemTime.getCurrentTime();
701 
702         uploadAtLimitStartTime = currTime;
703         downloadAtLimitStartTime = currTime;
704         isUploadMaxPinned = true;
705         isDownloadMaxPinned = true;
706     }
707 
resetPinSearch(SpeedManagerLimitEstimate estimate)708     void resetPinSearch(SpeedManagerLimitEstimate estimate){
709         //chocking ping needs higher limit.
710         float type = estimate.getEstimateType();
711         if(type>=SpeedManagerLimitEstimate.TYPE_CHOKE_ESTIMATED){
712             uploadChokePingCount++;
713         }
714         resetPinSearch();
715     }
716 
717     /**
718      * Return true if we are confidence testing the limits.
719      * @return - SMUpdate
720      */
isConfTestingLimits()721     public boolean isConfTestingLimits(){
722         return transferMode.isConfTestingLimits();
723     }//
724 
725     /**
726      * Determine if we have low confidence in this limit.
727      * @return - true if the confidence setting is LOW or NONE. Otherwise return true.
728      */
isDownloadConfidenceLow()729     public boolean isDownloadConfidenceLow(){
730         return ( downloadLimitConf.compareTo(SpeedLimitConfidence.MED) < 0 );
731     }
732 
isUploadConfidenceLow()733     public boolean isUploadConfidenceLow(){
734         return ( uploadLimitConf.compareTo(SpeedLimitConfidence.MED) < 0 );
735     }
736 
isDownloadConfidenceAbsolute()737     public boolean isDownloadConfidenceAbsolute(){
738         return ( downloadLimitConf.compareTo(SpeedLimitConfidence.ABSOLUTE)==0 );
739     }
740 
isUploadConfidenceAbsolute()741     public boolean isUploadConfidenceAbsolute(){
742         return ( uploadLimitConf.compareTo(SpeedLimitConfidence.ABSOLUTE)==0 );
743     }
744 
745 
746     /**
747      *
748      * @param downloadRate - currentUploadRate in bytes/sec
749      * @param uploadRate - currentUploadRate in bytes/sec
750      */
updateLimitTestingData( int downloadRate, int uploadRate )751     public synchronized void updateLimitTestingData( int downloadRate, int uploadRate ){
752         if( downloadRate>highestDownloadRate ){
753             highestDownloadRate=downloadRate;
754         }
755         if( uploadRate>highestUploadRate){
756             highestUploadRate=uploadRate;
757         }
758 
759         //The exit criteria for this test is 30 seconds without an increase in the limits.
760         long currTime = SystemTime.getCurrentTime();
761         if( currTime > clLastIncreaseTime+CONF_LIMIT_TEST_LENGTH){
762             //set the test done flag.
763             currTestDone=true;
764         }
765         // or 30 seconds after its first bad ping.
766         if( clFirstBadPingTime!=-1){
767             if( currTime > clFirstBadPingTime+CONF_LIMIT_TEST_LENGTH){
768                 //set the test done flag.
769                 currTestDone=true;
770             }
771         }
772 
773     }//updateLimitTestingData.
774 
775 
776     /**
777      * Convert raw ping value to new metric.
778      * @param lastMetric -
779      */
updateLimitTestingPing(int lastMetric)780     public void updateLimitTestingPing(int lastMetric){
781         //Convert raw - pings into a rating.
782         if(lastMetric>500){
783             updateLimitTestingPing(-1.0f);
784         }
785     }
786 
787     /**
788      * New metric from the PingMapper is value between -1.0 and +1.0f.
789      * @param lastMetric -
790      */
updateLimitTestingPing(float lastMetric)791     public void updateLimitTestingPing(float lastMetric){
792         if( lastMetric<-0.3f){
793             //Setting this time is a signal to end soon.
794             clFirstBadPingTime = SystemTime.getCurrentTime();
795         }
796     }
797 
798 
799     /**
800      * Call this method to start the limit testing.
801      * @param currUploadLimit -
802      * @param currDownloadLimit -
803      * @return - SMUpdate
804      */
startLimitTesting(int currUploadLimit, int currDownloadLimit)805     public SMUpdate startLimitTesting(int currUploadLimit, int currDownloadLimit){
806 
807         clLastIncreaseTime =SystemTime.getCurrentTime();
808         clFirstBadPingTime =-1;
809 
810         highestUploadRate=0;
811         highestDownloadRate=0;
812         currTestDone=false;
813 
814         //reset the flag.
815         beginLimitTest=false;
816 
817         //get the limits before the test, we are restoring them after the test.
818         preTestUploadLimit = currUploadLimit;
819         preTestDownloadLimit = currDownloadLimit;
820 
821         //configure the limits for this test. One will be at min and the other unlimited.
822         SMUpdate retVal;
823         if( transferMode.isDownloadMode() ){
824             //test the download limit.
825             retVal = new SMUpdate(uploadLimitMin,true,
826                         Math.round(downloadLimitMax *1.2f),true);
827             preTestDownloadCapacity = downloadLimitMax;
828             transferMode.setMode( TransferMode.State.DOWNLOAD_LIMIT_SEARCH );
829         }else{
830             //test the upload limit.
831             retVal = new SMUpdate( Math.round(uploadLimitMax *1.2f),true,
832                         downloadLimitMin,true);
833             preTestUploadCapacity = uploadLimitMax;
834             transferMode.setMode( TransferMode.State.UPLOAD_LIMIT_SEARCH );
835         }
836 
837         return retVal;
838     }
839 
840     /**
841      * Ramp the upload and download rates higher, so ping-times are relevant.
842      * @param uploadLimit -
843      * @param downloadLimit -
844      * @return -
845      */
rampTestingLimit(int uploadLimit, int downloadLimit)846     public SMUpdate rampTestingLimit(int uploadLimit, int downloadLimit){
847         SMUpdate retVal;
848         if( transferMode.getMode() == TransferMode.State.DOWNLOAD_LIMIT_SEARCH
849                 && downloadBandwidthStatus.isGreater( SaturatedMode.MED ) )
850         {
851             downloadLimit *= 1.1f;
852             clLastIncreaseTime = SystemTime.getCurrentTime();
853             retVal = new SMUpdate(uploadLimit,false,downloadLimit,true);
854 
855         }else if( transferMode.getMode() == TransferMode.State.UPLOAD_LIMIT_SEARCH
856                 && uploadBandwidthStatus.isGreater( SaturatedMode.MED ))
857         {
858             uploadLimit *= 1.1f;
859             clLastIncreaseTime = SystemTime.getCurrentTime();
860             retVal = new SMUpdate(uploadLimit,true,downloadLimit,false);
861 
862         }else{
863             retVal = new SMUpdate(uploadLimit,false,downloadLimit,false);
864             SpeedManagerLogger.trace("ERROR: rampTestLimit should only be called during limit testing. ");
865         }
866 
867         return retVal;
868     }//rampTestingLimit
869 
triggerLimitTestingFlag()870     public void triggerLimitTestingFlag(){
871         SpeedManagerLogger.trace("triggerd fast limit test.");
872         beginLimitTest=true;
873 
874         //if we are using a persistent PingSource then get that here.
875         if( useVariancePingMap ){
876             SMInstance pm = SMInstance.getInstance();
877             SpeedManagerAlgorithmProviderAdapter adapter = pm.getAdapter();
878 
879             //start a new transientPingMap;
880             if(transientPingMap!=null){
881                 transientPingMap.destroy();
882             }
883             transientPingMap = adapter.createTransientPingMapper();
884         }
885 
886     }
887 
isStartLimitTestFlagSet()888     public synchronized boolean isStartLimitTestFlagSet(){
889         return beginLimitTest;
890     }
891 
isConfLimitTestFinished()892     public synchronized boolean isConfLimitTestFinished(){
893         return currTestDone;
894     }
895 
endLimitTesting(int downloadCapacityGuess, int uploadCapacityGuess)896     public synchronized SMUpdate endLimitTesting(int downloadCapacityGuess, int uploadCapacityGuess){
897 
898         SpeedManagerLogger.trace(" repalce highestDownloadRate: "+highestDownloadRate+" with "+downloadCapacityGuess);
899         SpeedManagerLogger.trace(" replace highestUploadRate: "+highestUploadRate+" with "+uploadCapacityGuess);
900 
901         highestDownloadRate = downloadCapacityGuess;
902         highestUploadRate = uploadCapacityGuess;
903 
904         return endLimitTesting();
905     }
906 
907     /**
908      * Call this method to end the limit testing.
909      * @return - SMUpdate
910      */
endLimitTesting()911     public synchronized SMUpdate endLimitTesting(){
912 
913         SMUpdate retVal;
914         //determine if the new setting is different then the old setting.
915         if( transferMode.getMode()==TransferMode.State.DOWNLOAD_LIMIT_SEARCH ){
916 
917             downloadLimitConf = determineConfidenceLevel();
918 
919             //set that value.
920             SpeedManagerLogger.trace("pre-upload-setting="+ preTestUploadCapacity +" up-capacity"+ uploadLimitMax
921                     +" pre-download-setting="+ preTestDownloadCapacity +" down-capacity="+ downloadLimitMax);
922 
923             retVal = new SMUpdate(preTestUploadLimit,true, downloadLimitMax,true);
924             //change back to original mode.
925             transferMode.setMode( TransferMode.State.DOWNLOADING );
926 
927         }else if( transferMode.getMode()==TransferMode.State.UPLOAD_LIMIT_SEARCH){
928 
929             uploadLimitConf = determineConfidenceLevel();
930 
931             //set that value.
932             retVal = new SMUpdate(uploadLimitMax,true, downloadLimitMax,true);
933             //change back to original mode.
934             transferMode.setMode( TransferMode.State.SEEDING );
935 
936         }else{
937             //This is an "illegal state" make it in the logs, but try to recover by setting back to original state.
938             SpeedManagerLogger.log("SpeedLimitMonitor had IllegalState during endLimitTesting.");
939             retVal = new SMUpdate(preTestUploadLimit,true, preTestDownloadLimit,true);
940         }
941 
942         currTestDone=true;
943 
944         //reset the counter
945         uploadAtLimitStartTime = SystemTime.getCurrentTime();
946         downloadAtLimitStartTime = SystemTime.getCurrentTime();
947 
948         return retVal;
949     }
950 
951     /**
952      * After a test is complete determine how condifent the client should be in it
953      * based on how different it is from the previous result.  If the new result is within
954      * 20% of the old result then give it a MED. If it is great then give it a LOW.
955      * @return - what the new confidence interval should be.
956      */
determineConfidenceLevel()957     public SpeedLimitConfidence determineConfidenceLevel(){
958         SpeedLimitConfidence retVal=SpeedLimitConfidence.NONE;
959         String settingMaxLimitName;
960         //String settingMinLimitName;
961         boolean isDownload;
962         String settingConfidenceName;
963         int preTestValue;
964         int highestValue;
965         if(transferMode.getMode()==TransferMode.State.DOWNLOAD_LIMIT_SEARCH){
966 
967             settingConfidenceName = DOWNLOAD_CONF_LIMIT_SETTING;
968             settingMaxLimitName = SpeedManagerAlgorithmProviderV2.SETTING_DOWNLOAD_MAX_LIMIT;
969             isDownload=true;
970             preTestValue = preTestDownloadCapacity;
971             highestValue = highestDownloadRate;
972         }else if(transferMode.getMode()==TransferMode.State.UPLOAD_LIMIT_SEARCH){
973 
974             settingConfidenceName = UPLOAD_CONF_LIMIT_SETTING;
975             settingMaxLimitName = SpeedManagerAlgorithmProviderV2.SETTING_UPLOAD_MAX_LIMIT;
976             isDownload=false;
977             preTestValue = preTestUploadCapacity;
978             highestValue = highestUploadRate;
979         }else{
980             //
981             SpeedManagerLogger.log("IllegalState in determineConfidenceLevel(). Setting level to NONE.");
982             return SpeedLimitConfidence.NONE;
983         }
984 
985         boolean hadChockingPing = hadChockingPing();
986         float percentDiff = (float)Math.abs( highestValue-preTestValue )/(float)(Math.max(highestValue,preTestValue));
987         if( percentDiff<0.15f  && hadChockingPing ){
988             //Only set to medium if had both a chocking ping and two tests with similar results.
989             retVal = SpeedLimitConfidence.MED;
990         }else{
991             retVal = SpeedLimitConfidence.LOW;
992         }
993 
994         //update the values.
995         COConfigurationManager.setParameter(settingConfidenceName, retVal.getString() );
996         int newMaxLimitSetting = highestValue;
997         COConfigurationManager.setParameter(settingMaxLimitName, newMaxLimitSetting);
998         int newMinLimitSetting;
999         if( isDownload ){
1000             newMinLimitSetting = SMConst.calculateMinDownload( newMaxLimitSetting );
1001         }else{
1002             newMinLimitSetting = SMConst.calculateMinUpload( newMaxLimitSetting );
1003         }
1004 
1005         StringBuffer sb = new StringBuffer();
1006         if( transferMode.getMode()==TransferMode.State.UPLOAD_LIMIT_SEARCH ){
1007             sb.append("new upload limits: ");
1008             uploadLimitMax =newMaxLimitSetting;
1009             uploadLimitMin=newMinLimitSetting;
1010             //downloadCapacity can never be less then upload capacity.
1011             if( downloadLimitMax < uploadLimitMax){
1012                 downloadLimitMax = uploadLimitMax;
1013                 COConfigurationManager.setParameter(
1014                         SpeedManagerAlgorithmProviderV2.SETTING_DOWNLOAD_MAX_LIMIT, downloadLimitMax);
1015             }
1016             sb.append(uploadLimitMax);
1017         }else{
1018             sb.append("new download limits: ");
1019             downloadLimitMax =newMaxLimitSetting;
1020             downloadLimitMin=newMinLimitSetting;
1021             //upload capacity should never be 40x less then download.
1022             if( uploadLimitMax * 40 < downloadLimitMax){
1023                 uploadLimitMax = downloadLimitMax /40;
1024                 COConfigurationManager.setParameter(
1025                          SpeedManagerAlgorithmProviderV2.SETTING_UPLOAD_MAX_LIMIT, uploadLimitMax);
1026 
1027                 uploadLimitMin = SMConst.calculateMinUpload( uploadLimitMax );
1028             }//if
1029             sb.append(downloadLimitMax);
1030         }
1031 
1032         slider.updateLimits(uploadLimitMax,uploadLimitMin,downloadLimitMax,downloadLimitMin);
1033 
1034         SpeedManagerLogger.trace( sb.toString() );
1035 
1036         return retVal;
1037     }
1038 
1039     /**
1040      * If the user changes the line capacity settings on the configuration panel and adjustment
1041      * needs to occur even if the signal is NO-CHANGE-NEEDED. Test for that condition here.
1042      * @param currUploadLimit  - reported upload capacity from the adapter
1043      * @param currDownloadLimit - reported download capacity from the adapter.
1044      * @return - true if the "capacity" is lower then the current limit.
1045      */
areSettingsInSpec(int currUploadLimit, int currDownloadLimit)1046     public boolean areSettingsInSpec(int currUploadLimit, int currDownloadLimit){
1047 
1048         //during a confidence level test, anything goes.
1049         if( isConfTestingLimits() ){
1050             return true;
1051         }
1052 
1053         boolean retVal = true;
1054         if( currUploadLimit> uploadLimitMax){
1055             retVal = false;
1056         }
1057         if(  currDownloadLimit> downloadLimitMax && slider.isDownloadUnlimitedMode() ){
1058             retVal = false;
1059         }
1060         return retVal;
1061     }
1062 
choseBestLimit(SpeedManagerLimitEstimate estimate, int currMaxLimit, SpeedLimitConfidence currConf)1063     private int choseBestLimit(SpeedManagerLimitEstimate estimate, int currMaxLimit, SpeedLimitConfidence currConf) {
1064         float type = estimate.getEstimateType();
1065         int estBytesPerSec = estimate.getBytesPerSec();
1066         int chosenLimit;
1067 
1068         //no estimate less then 20k accepted.
1069         if( (estBytesPerSec<currMaxLimit) && estBytesPerSec<20480 ){
1070             return currMaxLimit;
1071         }
1072 
1073         String reason="";
1074         if(  type==SpeedManagerLimitEstimate.TYPE_MANUAL){
1075             chosenLimit = estBytesPerSec;
1076             reason="manual";
1077         }else if( type==SpeedManagerLimitEstimate.TYPE_UNKNOWN){
1078             chosenLimit = Math.max( estBytesPerSec, currMaxLimit );
1079             reason="unknown";
1080         }else if (type==SpeedManagerLimitEstimate.TYPE_ESTIMATED ){
1081 
1082         	if ( estimate.getMetricRating() >= 0.0 ){
1083 
1084         			// things looking good, this is just a new limit estimate and shouldn't
1085         			// affect the actual limit in force
1086 
1087            		return( currMaxLimit );
1088 
1089         	}else{
1090 
1091         		chosenLimit = estBytesPerSec;
1092         		reason = "estimate and bad metric";
1093          	}
1094 
1095         }else{
1096             //chose ping mapper.
1097             chosenLimit = estBytesPerSec;
1098         }
1099 
1100         SpeedManagerLogger.trace("bestChosenLimit: reason="+reason+",chosenLimit="+chosenLimit);
1101 
1102         return chosenLimit;
1103     }
1104 
1105     /**
1106      * Make some choices about how usable the limits are before passing them on.
1107      * @param estUp -
1108      * @param estDown -
1109      */
setRefLimits(SpeedManagerLimitEstimate estUp,SpeedManagerLimitEstimate estDown)1110     public void setRefLimits(SpeedManagerLimitEstimate estUp,SpeedManagerLimitEstimate estDown){
1111 
1112         SpeedManagerLimitEstimate up = SMConst.filterEstimate(estUp,SMConst.MIN_UPLOAD_BYTES_PER_SEC);
1113         int upMax = choseBestLimit(up, uploadLimitMax, uploadLimitConf);
1114 
1115         SpeedManagerLimitEstimate down = SMConst.filterEstimate(estDown, SMConst.MIN_DOWNLOAD_BYTES_PER_SEC);
1116         int downMax = choseBestLimit(down, downloadLimitMax, downloadLimitConf);
1117 
1118         if(downMax<upMax){
1119             SpeedManagerLogger.trace("down max-limit was less then up-max limit. increasing down max-limit. upMax="
1120                     +upMax+" downMax="+downMax);
1121             downMax = upMax;
1122         }
1123 
1124         setRefLimits(upMax,downMax);
1125     }
1126 
setRefLimits(int uploadMax, int downloadMax)1127     public void setRefLimits(int uploadMax, int downloadMax){
1128 
1129         if( (uploadLimitMax!=uploadMax) && (uploadMax>0) ){
1130             uploadLimitMax=uploadMax;
1131             COConfigurationManager.setParameter(
1132                     SpeedManagerAlgorithmProviderV2.SETTING_UPLOAD_MAX_LIMIT, uploadLimitMax);
1133         }
1134 
1135         uploadLimitMin = SMConst.calculateMinUpload( uploadMax );
1136 
1137         if( (downloadLimitMax!=downloadMax) && (downloadMax>0) ){
1138             downloadLimitMax = downloadMax;
1139             COConfigurationManager.setParameter(
1140                     SpeedManagerAlgorithmProviderV2.SETTING_DOWNLOAD_MAX_LIMIT, downloadLimitMax);
1141         }
1142 
1143         downloadLimitMin = SMConst.calculateMinDownload(downloadMax);
1144 
1145         SpeedManagerLogger.trace("setRefLimits uploadMax="+uploadMax+" uploadLimitMax="+uploadLimitMax+", downloadMax="+downloadMax+" downloadLimitMax="+downloadLimitMax);
1146 
1147         slider.updateLimits(uploadLimitMax,uploadLimitMin,downloadLimitMax,downloadLimitMin);
1148     }
1149 
1150     /**
1151      * It is likely the user adjusted the "line speed capacity" on the configuration panel.
1152      * We need to adjust the current limits down to adjust.
1153      * @param currUploadLimit -
1154      * @param currDownloadLimit -
1155      * @return - Updates as needed.
1156      */
adjustLimitsToSpec(int currUploadLimit, int currDownloadLimit)1157     public SMUpdate adjustLimitsToSpec(int currUploadLimit, int currDownloadLimit){
1158 
1159         int newUploadLimit = currUploadLimit;
1160         boolean uploadChanged = false;
1161         int newDownloadLimit = currDownloadLimit;
1162         boolean downloadChanged = false;
1163 
1164         StringBuffer reason = new StringBuffer();
1165 
1166         //check for the case when the line-speed capacity is below the current limit.
1167         if( currUploadLimit> uploadLimitMax && uploadLimitMax!=0){
1168 
1169             newUploadLimit = uploadLimitMax;
1170             uploadChanged = true;
1171 
1172             reason.append(" (a) upload line-speed cap below current limit. ");
1173         }
1174 
1175         if(uploadLimitMax==0){
1176             reason.append("** uploadLimitMax=0 (Unlimited)! ** ");
1177         }
1178 
1179         //check for the case when the min setting has been moved above the current limit.
1180         if( currDownloadLimit> downloadLimitMax && !slider.isDownloadUnlimitedMode() ){
1181             newDownloadLimit = downloadLimitMax;
1182             downloadChanged = true;
1183 
1184             reason.append(" (b) download line-speed cap below current limit. ");
1185         }
1186 
1187         //Another possibility is the min limits have been raised.
1188         if( currUploadLimit<uploadLimitMin ){
1189             newUploadLimit = uploadLimitMin;
1190             uploadChanged = true;
1191 
1192             reason.append(" (c) min upload limit raised. ");
1193         }
1194 
1195         if( currDownloadLimit<downloadLimitMin ){
1196             newDownloadLimit = downloadLimitMin;
1197             downloadChanged = true;
1198 
1199             reason.append(" (d)  min download limit raised. ");
1200         }
1201 
1202         SpeedManagerLogger.trace("Adjusting limits due to out of spec: new-up="+newUploadLimit
1203                 +" new-down="+newDownloadLimit+"  reasons: "+reason.toString());
1204 
1205         return new SMUpdate(newUploadLimit,uploadChanged,newDownloadLimit,downloadChanged);
1206     }
1207 
1208 
log(String str)1209     protected void log(String str){
1210 
1211         SpeedManagerLogger.log(str);
1212     }//log
1213 
1214 
1215 
initPingSpaceMap(int maxGoodPing, int minBadPing)1216     public void initPingSpaceMap(int maxGoodPing, int minBadPing){
1217         pingMapOfDownloadMode = new PingSpaceMapper(maxGoodPing,minBadPing);
1218         pingMapOfSeedingMode = new PingSpaceMapper(maxGoodPing,minBadPing);
1219 
1220         //pingMonitor = new PingSpaceMonitor(maxGoodPing,minBadPing,transferMode);
1221 
1222         useVariancePingMap = false;
1223     }
1224 
initPingSpaceMap()1225     public void initPingSpaceMap(){
1226         useVariancePingMap = true;
1227 
1228 
1229         //ToDo: remove after beta-testing - just to characterize the different methods.
1230 //        pingMapOfDownloadMode = new PingSpaceMapper(150,500);
1231 //        pingMapOfSeedingMode = new PingSpaceMapper(150,500);
1232 
1233     }
1234 
1235     /**
1236      * This is a lot of data, but is important debug info.
1237      * @param name -
1238      * @param transEst -
1239      * @param hadChockPing -
1240      * @param permEst -
1241      * @param downMode -
1242      * @param seedMode -
1243      */
betaLogPingMapperEstimates(String name, SpeedManagerLimitEstimate transEst, boolean hadChockPing, SpeedManagerLimitEstimate permEst, PingSpaceMapper downMode, PingSpaceMapper seedMode)1244     public void betaLogPingMapperEstimates(String name,
1245                                            SpeedManagerLimitEstimate transEst,
1246                                            boolean hadChockPing,
1247                                            SpeedManagerLimitEstimate permEst,
1248                                            PingSpaceMapper downMode,
1249                                            PingSpaceMapper seedMode)
1250     {
1251         StringBuffer sb = new StringBuffer("beta-ping-maps-").append(name).append(": ");
1252 
1253         if(transEst!=null){
1254             int rate = transEst.getBytesPerSec();
1255             float conf = transEst.getMetricRating();
1256             sb.append("transient-").append(rate).append("(").append(conf).append(")");
1257         }
1258         sb.append(" chockPing=").append(hadChockPing);
1259 
1260 
1261         if(permEst!=null){
1262             int rate = permEst.getBytesPerSec();
1263             float conf = permEst.getMetricRating();
1264             sb.append("; perm-").append(rate).append("(").append(conf).append(")");
1265         }
1266 
1267         if(downMode!=null){
1268             int rateDown = downMode.guessDownloadLimit();
1269             int rateUp = downMode.guessUploadLimit();
1270             boolean downChockPing = downMode.hadChockingPing(true);
1271             boolean upChockPing = downMode.hadChockingPing(false);
1272 
1273             sb.append("; downMode- ");
1274             sb.append("rateDown=").append(rateDown).append(" ");
1275             sb.append("rateUp=").append(rateUp).append(" ");
1276             sb.append("downChockPing=").append(downChockPing).append(" ");
1277             sb.append("upChockPing=").append(upChockPing).append(" ");
1278         }
1279 
1280         if(seedMode!=null){
1281             int rateDown = seedMode.guessDownloadLimit();
1282             int rateUp = seedMode.guessUploadLimit();
1283             boolean downChockPing = seedMode.hadChockingPing(true);
1284             boolean upChockPing = seedMode.hadChockingPing(false);
1285 
1286             sb.append("; seedMode- ");
1287             sb.append("rateDown=").append(rateDown).append(" ");
1288             sb.append("rateUp=").append(rateUp).append(" ");
1289             sb.append("downChockPing=").append(downChockPing).append(" ");
1290             sb.append("upChockPing=").append(upChockPing).append(" ");
1291         }
1292         SpeedManagerLogger.log( sb.toString() );
1293     }//betaLogPingMapperEstimates
1294 
guessDownloadLimit()1295     public int guessDownloadLimit(){
1296 
1297         if( !useVariancePingMap){
1298             return pingMapOfDownloadMode.guessDownloadLimit();
1299         }else{
1300 
1301             boolean wasChocked=true;
1302             SpeedManagerLimitEstimate transientEst=null;
1303             if(transientPingMap!=null){
1304                 transientEst = transientPingMap.getLastBadDownloadLimit();
1305                 if(transientEst==null){
1306                     wasChocked=false;
1307                     transientEst = transientPingMap.getEstimatedDownloadLimit(false);
1308                 }
1309             }
1310 
1311             //NOTE: Currently just getting the persistentMap for temp logging purposes.
1312             SMInstance pm = SMInstance.getInstance();
1313             SpeedManagerAlgorithmProviderAdapter adapter = pm.getAdapter();
1314             SpeedManagerPingMapper persistentMap = adapter.getPingMapper();
1315             SpeedManagerLimitEstimate persistentEst = persistentMap.getEstimatedDownloadLimit(false);
1316 
1317             //log the different ping-mappers for beta.
1318             betaLogPingMapperEstimates("down",transientEst,wasChocked,persistentEst,pingMapOfDownloadMode,pingMapOfSeedingMode);
1319 
1320             if( transientEst!=null )
1321             {
1322                 return choseBestLimit(transientEst,downloadLimitMax,downloadLimitConf);
1323             }else{
1324                 return downloadLimitMax;
1325             }
1326 
1327         }
1328     }//guessDownloadLimit
1329 
guessUploadLimit()1330     public int guessUploadLimit(){
1331 
1332         if( !useVariancePingMap){
1333 
1334             int dmUpLimitGuess = pingMapOfDownloadMode.guessUploadLimit();
1335             int smUpLimitGuess = pingMapOfSeedingMode.guessUploadLimit();
1336 
1337             return Math.max(dmUpLimitGuess,smUpLimitGuess);
1338 
1339         }else{
1340 
1341             boolean wasChocked=true;
1342             SpeedManagerLimitEstimate transientEst=null;
1343             if(transientPingMap!=null){
1344                 transientEst = transientPingMap.getLastBadUploadLimit();
1345                 if(transientEst==null){
1346                     wasChocked=false;
1347                     transientEst = transientPingMap.getEstimatedUploadLimit(false);
1348                 }
1349             }
1350 
1351             //NOTE: Currently just getting the persistentMap for temp logging purposes.
1352             SMInstance pm = SMInstance.getInstance();
1353             SpeedManagerAlgorithmProviderAdapter adapter = pm.getAdapter();
1354             SpeedManagerPingMapper persistentMap = adapter.getPingMapper();
1355             SpeedManagerLimitEstimate persistentEst = persistentMap.getEstimatedUploadLimit(false);
1356 
1357             //log the different ping-mappers for beta.
1358             betaLogPingMapperEstimates("up",transientEst,wasChocked,persistentEst,pingMapOfDownloadMode,pingMapOfSeedingMode);
1359 
1360             if( transientEst!=null )
1361             {
1362                 return choseBestLimit(transientEst,uploadLimitMax,uploadLimitConf);
1363             }else{
1364                 return uploadLimitMax;
1365             }
1366         }
1367 
1368     }//guessUploadLimit
1369 
1370 
1371     /**
1372      * Should return true if had a recent chocking ping.
1373      * @return - true if
1374      */
hadChockingPing()1375     public boolean hadChockingPing(){
1376         if( !useVariancePingMap){
1377 
1378             return pingMapOfDownloadMode.hadChockingPing(true);
1379 
1380         }else{
1381             SpeedManagerPingMapper pm = SMInstance.getInstance().getAdapter().getPingMapper();
1382 
1383             //if either had a choking ping.
1384             SpeedManagerLimitEstimate dEst = pm.getEstimatedDownloadLimit(true);
1385             SpeedManagerLimitEstimate uEst = pm.getEstimatedUploadLimit(true);
1386 
1387             boolean hadChokePingUp = (uEst.getEstimateType()==SpeedManagerLimitEstimate.TYPE_CHOKE_ESTIMATED);
1388             boolean hadChokePingDown = (dEst.getEstimateType()==SpeedManagerLimitEstimate.TYPE_CHOKE_ESTIMATED);
1389 
1390             return ( hadChokePingUp || hadChokePingDown );
1391         }
1392     }//hadChockingPing
1393 
1394     /**
1395      * Just log this data until we decide if it is useful.
1396      */
logPingMapData()1397     public void logPingMapData() {
1398 
1399         if( !useVariancePingMap){
1400             int downLimGuess = pingMapOfDownloadMode.guessDownloadLimit();
1401             int upLimGuess = pingMapOfDownloadMode.guessUploadLimit();
1402             int seedingUpLimGuess = pingMapOfSeedingMode.guessUploadLimit();
1403 
1404             StringBuffer sb = new StringBuffer("ping-map: ");
1405             sb.append(":down=").append(downLimGuess);
1406             sb.append(":up=").append(upLimGuess);
1407             sb.append(":(seed)up=").append(seedingUpLimGuess);
1408 
1409             SpeedManagerLogger.log( sb.toString()  );
1410         }else{
1411             SMInstance pm = SMInstance.getInstance();
1412             SpeedManagerAlgorithmProviderAdapter adapter = pm.getAdapter();
1413             SpeedManagerPingMapper persistentMap = adapter.getPingMapper();
1414 
1415             SpeedManagerLimitEstimate estUp = persistentMap.getEstimatedUploadLimit(false);
1416             SpeedManagerLimitEstimate estDown = persistentMap.getEstimatedDownloadLimit(false);
1417 
1418             int downLimGuess = estDown.getBytesPerSec();
1419             float downConf = estDown.getMetricRating();
1420             int upLimGuess = estUp.getBytesPerSec();
1421             float upConf = estUp.getMetricRating();
1422 
1423             String name = persistentMap.getName();
1424 
1425             StringBuffer sb = new StringBuffer("new-ping-map: ");
1426             sb.append(" name=").append(name);
1427             sb.append(", down=").append(downLimGuess);
1428             sb.append(", down-conf=").append(downConf);
1429             sb.append(", up=").append(upLimGuess);
1430             sb.append(", up-conf=").append(upConf);
1431 
1432             SpeedManagerLogger.log( sb.toString() );
1433         }
1434     }//logPingMapData
1435 
setCurrentTransferRates(int downRate, int upRate)1436     public void setCurrentTransferRates(int downRate, int upRate){
1437 
1438         if( pingMapOfDownloadMode!=null && pingMapOfSeedingMode!=null){
1439             pingMapOfDownloadMode.setCurrentTransferRates(downRate,upRate);
1440             pingMapOfSeedingMode.setCurrentTransferRates(downRate,upRate);
1441         }
1442 
1443     }
1444 
resetPingSpace()1445     public void resetPingSpace(){
1446 
1447         if( pingMapOfDownloadMode!=null && pingMapOfSeedingMode!=null){
1448             pingMapOfDownloadMode.reset();
1449             pingMapOfSeedingMode.reset();
1450         }
1451 
1452         if(transientPingMap!=null){
1453             transientPingMap.destroy();
1454         }
1455     }
1456 
addToPingMapData(int lastMetricValue)1457     public void addToPingMapData(int lastMetricValue){
1458         String modeStr = getTransferModeAsString();
1459 
1460         if(    modeStr.equalsIgnoreCase(TransferMode.State.DOWNLOADING.getString())
1461             || modeStr.equalsIgnoreCase(TransferMode.State.DOWNLOAD_LIMIT_SEARCH.getString())  )
1462         {
1463             //add point to map for download mode
1464             pingMapOfDownloadMode.addMetricToMap(lastMetricValue);
1465 
1466         }
1467         else if(     modeStr.equalsIgnoreCase(TransferMode.State.SEEDING.getString())
1468                   || modeStr.equalsIgnoreCase(TransferMode.State.UPLOAD_LIMIT_SEARCH.getString()) )
1469         {
1470             //add point to map for seeding mode.
1471             pingMapOfSeedingMode.addMetricToMap(lastMetricValue);
1472 
1473         }
1474 
1475 
1476         //if confidence limit testing, inform of bad ping.
1477         updateLimitTestingPing(lastMetricValue);
1478 
1479         longTermMonitor.updateStatus(transferMode);
1480 
1481     }//addToPingMapData
1482 
1483 
notifyUpload(SpeedManagerLimitEstimate estimate)1484     public void notifyUpload(SpeedManagerLimitEstimate estimate) {
1485         int bestLimit = choseBestLimit(estimate,uploadLimitMax,uploadLimitConf);
1486 
1487         SpeedManagerLogger.trace("notifyUpload uploadLimitMax="+uploadLimitMax);
1488         tempLogEstimate(estimate);
1489 
1490         if(bestLimit!=uploadLimitMax){
1491             //update COConfiguration
1492             SpeedManagerLogger.log("persistent PingMap changed upload limit to "+bestLimit);
1493 
1494             resetPinSearch(estimate);
1495 
1496             uploadLimitMax = bestLimit;
1497             COConfigurationManager.setParameter(
1498                     SpeedManagerAlgorithmProviderV2.SETTING_UPLOAD_MAX_LIMIT, uploadLimitMax);
1499         }
1500 
1501         uploadLimitMin = SMConst.calculateMinUpload(uploadLimitMax);
1502         slider.updateLimits(uploadLimitMax,uploadLimitMin,downloadLimitMax,downloadLimitMin);
1503 
1504         SMSearchLogger.log("new upload rate: "+uploadLimitMax);
1505     }
1506 
notifyDownload(SpeedManagerLimitEstimate estimate)1507     public void notifyDownload(SpeedManagerLimitEstimate estimate) {
1508         int bestLimit = choseBestLimit(estimate,downloadLimitMax,downloadLimitConf);
1509 
1510         SpeedManagerLogger.trace("notifyDownload downloadLimitMax="+downloadLimitMax
1511                 +" conf="+downloadLimitConf.getString()+" ("+downloadLimitConf.asEstimateType()+")");
1512         tempLogEstimate(estimate);
1513 
1514         if(downloadLimitMax!=bestLimit){
1515             //update COConfiguration
1516             SpeedManagerLogger.log( "persistent PingMap changed download limit to "+bestLimit );
1517             downloadLimitMax = bestLimit;
1518             COConfigurationManager.setParameter(
1519                     SpeedManagerAlgorithmProviderV2.SETTING_DOWNLOAD_MAX_LIMIT, bestLimit);
1520         }
1521 
1522         downloadLimitMin = SMConst.calculateMinDownload(downloadLimitMax);
1523         slider.updateLimits(uploadLimitMax,uploadLimitMin,downloadLimitMax,downloadLimitMin);
1524 
1525 
1526         if(estimate.getBytesPerSec()!=0){
1527             slider.setDownloadUnlimitedMode(false);
1528         }else{
1529             slider.setDownloadUnlimitedMode(true);
1530         }
1531 
1532         SMSearchLogger.log("download "+downloadLimitMax );
1533     }
1534 
tempLogEstimate(SpeedManagerLimitEstimate est)1535     private void tempLogEstimate(SpeedManagerLimitEstimate est){
1536 
1537         if(est==null){
1538             SpeedManagerLogger.trace( "notify log: SpeedManagerLimitEstimate was null" );
1539             return;
1540         }
1541 
1542         StringBuffer sb = new StringBuffer();
1543         float metric = est.getMetricRating();
1544         float type = est.getEstimateType();
1545         int rate = est.getBytesPerSec();
1546 
1547         String str = est.getString();
1548 
1549         sb.append("notify log: ").append(str);
1550         sb.append(" metricRating=").append(metric);
1551         sb.append(" rate=").append(rate);
1552         sb.append(" type=").append(type);
1553 
1554         SpeedManagerLogger.trace( sb.toString() );
1555 
1556     }//tempLogEstimate
1557 
1558 }//SpeedLimitMonitor
1559