1 /* 2 * Copyright 2014 Intel Corporation All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.intel.thermal; 18 19 import org.xmlpull.v1.XmlPullParser; 20 import org.xmlpull.v1.XmlPullParserException; 21 import org.xmlpull.v1.XmlPullParserFactory; 22 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.os.SystemProperties; 28 import android.util.Log; 29 30 import java.io.FileNotFoundException; 31 import java.io.FileReader; 32 import java.io.IOException; 33 import java.lang.reflect.InvocationTargetException; 34 import java.lang.reflect.Method; 35 import java.util.ArrayList; 36 import java.util.Hashtable; 37 38 /** 39 * The ThermalCooling class parses the thermal_throttle_config.xml. This class 40 * receives Thermal Intents and takes appropriate actions based on the policies 41 * configured in the xml file. 42 * 43 * @hide 44 */ 45 public class ThermalCooling { 46 private static final String TAG = "ThermalCooling"; 47 private static final String THERMAL_SHUTDOWN_NOTIFY_PATH = 48 "/sys/module/intel_mid_osip/parameters/force_shutdown_occured"; 49 50 private Context mContext; 51 52 // count to keep track of zones in critical state, waiting for shutdown 53 private int mCriticalZonesCount = 0; 54 private static final Object sCriticalZonesCountLock = new Object(); 55 56 private ThermalZoneReceiver mThermalIntentReceiver = new ThermalZoneReceiver(); 57 private ProfileChangeReceiver mProfChangeReceiver = new ProfileChangeReceiver(); 58 private boolean mProfChangeListenerInitialized = false; 59 /** 60 * This is the parser class which parses the thermal_throttle_config.xml 61 * file. 62 */ 63 protected enum MetaTag { 64 ENUM_THROTTLEVALUES, 65 ENUM_THROTTLEMASK, 66 ENUM_DETHROTTLEMASK, 67 ENUM_UNKNOWN 68 } 69 70 public class ThermalParser { 71 private static final String THERMAL_THROTTLE_CONFIG = "thermalthrottleconfig"; 72 73 private static final String CDEVINFO = "ContributingDeviceInfo"; 74 75 private static final String ZONETHROTINFO = "ZoneThrottleInfo"; 76 77 private static final String COOLINGDEVICEINFO = "CoolingDeviceInfo"; 78 79 private static final String THROTTLEMASK = "ThrottleDeviceMask"; 80 81 private static final String DETHROTTLEMASK = "DethrottleDeviceMask"; 82 83 private static final String THROTTLEVALUES = "ThrottleValues"; 84 85 private static final String COOLINGDEVICESTATES = "CoolingDeviceStates"; 86 87 private static final String PROFILE = "Profile"; 88 89 private ArrayList<Integer> mTempMaskList; 90 91 private ArrayList<Integer> mTempThrottleValuesList;; 92 93 private boolean done = false; 94 95 XmlPullParserFactory mFactory; 96 97 XmlPullParser mParser; 98 99 ThermalCoolingDevice mDevice = null; 100 101 /* Hashtable of (ZoneID and ZoneCoolerBindingInfo object) */ 102 Hashtable<Integer, ThermalManager.ZoneCoolerBindingInfo> mZoneCoolerBindMap = null; 103 String mCurProfileName = ThermalManager.DEFAULT_PROFILE_NAME; 104 int mNumProfiles = 0; 105 106 ThermalManager.ZoneCoolerBindingInfo mZone = null; 107 108 FileReader mInputStream = null; 109 110 ThermalParser(String fname) { 111 try { 112 mFactory = XmlPullParserFactory.newInstance( 113 System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); 114 mFactory.setNamespaceAware(true); 115 mParser = mFactory.newPullParser(); 116 } catch (XmlPullParserException xppe) { 117 Log.e(TAG, "mParser NewInstance Exception"); 118 } 119 120 try { 121 mInputStream = new FileReader(fname); 122 if (mInputStream == null) 123 return; 124 if (mParser != null) { 125 mParser.setInput(mInputStream); 126 } 127 mDevice = null; 128 mZone = null; 129 } catch (XmlPullParserException xppe) { 130 Log.e(TAG, "mParser setInput XmlPullParserException"); 131 } catch (FileNotFoundException e) { 132 Log.e(TAG, "mParser setInput FileNotFoundException"); 133 } 134 135 } 136 137 ThermalParser() { 138 mParser = mContext.getResources(). 139 getXml(ThermalManager.sThrottleFileXmlId); 140 } 141 142 public boolean parse() { 143 if (ThermalManager.sIsOverlays == false && mInputStream == null) return false; 144 /* if mParser is null, close any open stream before exiting */ 145 if (mParser == null) { 146 try { 147 if (mInputStream != null) { 148 mInputStream.close(); 149 } 150 } catch (IOException e) { 151 Log.i(TAG, "IOException caught in parse() function"); 152 } 153 return false; 154 } 155 156 boolean ret = true; 157 MetaTag tag = MetaTag.ENUM_UNKNOWN; 158 try { 159 int mEventType = mParser.getEventType(); 160 while (mEventType != XmlPullParser.END_DOCUMENT && !done) { 161 switch (mEventType) { 162 case XmlPullParser.START_DOCUMENT: 163 Log.i(TAG, "StartDocument"); 164 break; 165 case XmlPullParser.START_TAG: 166 String tagName = mParser.getName(); 167 boolean isMetaTag = false; 168 if (tagName != null && tagName.equalsIgnoreCase(THROTTLEVALUES)) { 169 tag = MetaTag.ENUM_THROTTLEVALUES; 170 isMetaTag = true; 171 } else if (tagName != null && tagName.equalsIgnoreCase(THROTTLEMASK)) { 172 tag = MetaTag.ENUM_THROTTLEMASK; 173 isMetaTag = true; 174 } else if (tagName != null 175 && tagName.equalsIgnoreCase(DETHROTTLEMASK)) { 176 tag = MetaTag.ENUM_DETHROTTLEMASK; 177 isMetaTag = true; 178 } 179 if (isMetaTag) { 180 ret = processMetaTag(tagName, tag); 181 } else { 182 ret = processStartElement(tagName); 183 } 184 if (!ret) { 185 if (mInputStream != null) mInputStream.close(); 186 return false; 187 } 188 break; 189 case XmlPullParser.END_TAG: 190 processEndElement(mParser.getName()); 191 break; 192 } 193 mEventType = mParser.next(); 194 } 195 } catch (XmlPullParserException xppe) { 196 Log.i(TAG, "XmlPullParserException caught in parse():" + xppe.getMessage()); 197 ret = false; 198 } catch (IOException e) { 199 Log.i(TAG, "IOException caught in parse():" + e.getMessage()); 200 ret = false; 201 } finally { 202 try { 203 // end of parsing, close the stream 204 // close is moved here, since if there is an exception 205 // while parsing doc, input stream needs to be closed 206 if (mInputStream != null) { 207 mInputStream.close(); 208 } 209 } catch (IOException e) { 210 Log.i(TAG, "IOException caught in parse() function"); 211 ret = false; 212 } 213 return ret; 214 } 215 } 216 217 public boolean processMetaTag(String tagName, MetaTag tagId) { 218 if (mParser == null || tagName == null) return false; 219 ArrayList<Integer> tempList = new ArrayList<Integer>(); 220 try { 221 int eventType = mParser.next(); 222 while (true) { 223 if (eventType == XmlPullParser.START_TAG) { 224 tempList.add(Integer.parseInt(mParser.nextText())); 225 } else if (eventType == XmlPullParser.END_TAG && 226 mParser.getName().equalsIgnoreCase(tagName)) { 227 break; 228 } 229 eventType = mParser.next(); 230 } 231 } catch (XmlPullParserException xppe) { 232 Log.e(TAG, "XmlPullParserException:" + xppe.getMessage()); 233 return false; 234 } catch (IOException ioe) { 235 Log.e(TAG, "IOException:" + ioe.getMessage()); 236 return false; 237 } 238 239 switch(tagId) { 240 case ENUM_THROTTLEVALUES: 241 if (mDevice == null) { 242 return false; 243 } else { 244 // add throttle value for TCRITICAL (same as last value) 245 tempList.add(tempList.get(tempList.size() - 1)); 246 mDevice.setThrottleValuesList(tempList); 247 } 248 break; 249 case ENUM_THROTTLEMASK: 250 if (mZone == null || mZone.getLastCoolingDeviceInstance() == null) { 251 return false; 252 } else { 253 // Always throttle at CRITICAL state (last state) 254 tempList.add(1); 255 mZone.getLastCoolingDeviceInstance().setThrottleMaskList(tempList); 256 } 257 break; 258 case ENUM_DETHROTTLEMASK: 259 if (mZone == null || mZone.getLastCoolingDeviceInstance() == null) { 260 return false; 261 } else { 262 // Dethrottling at CRITICAL state (last state) is dontcare condition 263 tempList.add(0); 264 mZone.getLastCoolingDeviceInstance().setDeThrottleMaskList(tempList); 265 } 266 break; 267 default: 268 return false; 269 } 270 return true; 271 } 272 boolean processStartElement(String name) { 273 if (name == null) 274 return false; 275 boolean ret = true; 276 try { 277 if (name.equalsIgnoreCase(CDEVINFO)) { 278 if (mDevice == null) 279 mDevice = new ThermalCoolingDevice(); 280 } else if (name.equalsIgnoreCase(ZONETHROTINFO)) { 281 if (mZone == null) { 282 mZone = new ThermalManager.ZoneCoolerBindingInfo(); 283 } 284 if (mZoneCoolerBindMap == null) { 285 mZoneCoolerBindMap = new Hashtable<Integer, 286 ThermalManager.ZoneCoolerBindingInfo>(); 287 } 288 } else if (name.equalsIgnoreCase(PROFILE)) { 289 mNumProfiles++; 290 if (mZoneCoolerBindMap == null) { 291 mZoneCoolerBindMap = new Hashtable<Integer, 292 ThermalManager.ZoneCoolerBindingInfo>(); 293 } 294 } else if (name.equalsIgnoreCase(COOLINGDEVICEINFO) && mZone != null) { 295 if (mZone.getCoolingDeviceInfoList() == null) { 296 mZone.initializeCoolingDeviceInfoList(); 297 } 298 mZone.createNewCoolingDeviceInstance(); 299 } else { 300 // Retrieve zone and cooling device mapping 301 if (name.equalsIgnoreCase("ZoneID") && mZone != null) { 302 mZone.setZoneID(Integer.parseInt(mParser.nextText())); 303 } else if (name.equalsIgnoreCase("CriticalShutDown") && mZone != null) { 304 mZone.setCriticalActionShutdown(Integer.parseInt(mParser.nextText())); 305 } else if (name.equalsIgnoreCase(THROTTLEMASK) && mZone != null) { 306 mTempMaskList = new ArrayList<Integer>(); 307 } else if (name.equalsIgnoreCase(DETHROTTLEMASK) && mZone != null) { 308 mTempMaskList = new ArrayList<Integer>(); 309 } else if (name.equalsIgnoreCase("CoolingDevId") && mZone != null) { 310 mZone.getLastCoolingDeviceInstance().setCoolingDeviceId( 311 Integer.parseInt(mParser.nextText())); 312 } else if (name.equalsIgnoreCase(COOLINGDEVICESTATES) && mZone != null) { 313 // Increase cooling device states by 1, required for CRITICAL state 314 mZone.getLastCoolingDeviceInstance().setCoolingDeviceStates( 315 Integer.parseInt(mParser.nextText()) + 1); 316 } 317 // Retrieve cooling device information 318 if (name.equalsIgnoreCase("CDeviceName") && mDevice != null) { 319 mDevice.setDeviceName(mParser.nextText()); 320 } else if (name.equalsIgnoreCase("CDeviceID") && mDevice != null) { 321 mDevice.setDeviceId(Integer.parseInt(mParser.nextText())); 322 } else if (name.equalsIgnoreCase("CDeviceClassPath") && mDevice != null) { 323 mDevice.setClassPath(mParser.nextText()); 324 } else if (name.equalsIgnoreCase("CDeviceThrottlePath") && mDevice != null) { 325 mDevice.setThrottlePath(mParser.nextText()); 326 } else if (name.equalsIgnoreCase("Name")) { 327 mCurProfileName = mParser.nextText(); 328 } 329 } 330 } catch (XmlPullParserException e) { 331 Log.i(TAG, "XmlPullParserException caught in processStartElement()"); 332 ret = false; 333 } catch (IOException e) { 334 Log.i(TAG, "IOException caught in processStartElement()"); 335 ret = false; 336 } finally { 337 return ret; 338 } 339 } 340 341 void processEndElement(String name) { 342 if (name == null) 343 return; 344 if (name.equalsIgnoreCase(CDEVINFO) && mDevice != null) { 345 // if cooling dev suports less then DEFAULT throttle values donot add to map. 346 if (mDevice.getNumThrottleValues() < ThermalManager.DEFAULT_NUM_THROTTLE_VALUES) { 347 Log.i(TAG, "cooling dev:" + mDevice.getDeviceName() 348 + " deactivated! throttle values < " 349 + ThermalManager.DEFAULT_NUM_THROTTLE_VALUES); 350 mDevice = null; 351 return; 352 } 353 if (mDevice.getThrottlePath().equals("auto")) { 354 mDevice.setThrottlePath("auto"); 355 } 356 if (loadCoolingDevice(mDevice)) { 357 ThermalManager.sCDevMap.put(mDevice.getDeviceId(), mDevice); 358 } 359 mDevice = null; 360 } else if (name.equalsIgnoreCase(ZONETHROTINFO) && mZone != null) { 361 mZone.printAttributes(); 362 if (mZoneCoolerBindMap != null) { 363 mZoneCoolerBindMap.put(mZone.getZoneID(), mZone); 364 } 365 mZone = null; 366 } else if (name.equalsIgnoreCase(PROFILE)) { 367 if (mZoneCoolerBindMap != null) { 368 ThermalManager.sProfileBindMap.put(mCurProfileName, mZoneCoolerBindMap); 369 mZoneCoolerBindMap = new Hashtable<Integer, 370 ThermalManager.ZoneCoolerBindingInfo>(); 371 } 372 } else if (name.equalsIgnoreCase(THERMAL_THROTTLE_CONFIG)) { 373 Log.i(TAG, "Parsing Finished.."); 374 // This indicates we have not seen any <Profile> tag. 375 // Consider it as if we have only one 'Default' Profile. 376 if (mNumProfiles == 0 && mZoneCoolerBindMap != null) { 377 ThermalManager.sProfileBindMap.put(mCurProfileName, mZoneCoolerBindMap); 378 } 379 done = true; 380 } else if (name.equalsIgnoreCase(COOLINGDEVICEINFO) && mZone != null) { 381 ThermalManager.ZoneCoolerBindingInfo.CoolingDeviceInfo cDevInfo; 382 cDevInfo = mZone.getLastCoolingDeviceInstance(); 383 if (cDevInfo != null) { 384 ThermalCoolingDevice cDev = ThermalManager.sCDevMap 385 .get(cDevInfo.getCoolingDeviceId()); 386 if (cDev == null) return; 387 int cds = cDevInfo.getCoolingDeviceStates(); 388 // check the CDS against the number of throttle values exposed. 389 // If exceeds, cap it. 390 if (cds > cDev.getNumThrottleValues()) { 391 cDevInfo.setCoolingDeviceStates(cDev.getNumThrottleValues()); 392 Log.i(TAG, "capping cdevid: " + cDevInfo.getCoolingDeviceId() 393 + " to " + cDev.getNumThrottleValues() + " states"); 394 } 395 if (cDevInfo.checkMaskList(cDev.getNumThrottleValues())) { 396 // add only active cooling devices to list 397 mZone.addCoolingDeviceToList(cDevInfo); 398 } 399 } 400 } 401 } 402 } 403 404 private void configureDynamicTurbo() { 405 // Disable Dynamic Turbo based on the system property 406 int indx = ThermalUtils.getCoolingDeviceIndexContains("SoC"); 407 if (indx != -1 && !ThermalManager.sIsDynamicTurboEnabled) { 408 String path = ThermalManager.sCoolingDeviceBasePath + indx 409 + ThermalManager.sCoolingDeviceState; 410 ThermalUtils.writeSysfs(path, ThermalManager.DISABLE_DYNAMIC_TURBO); 411 } 412 } 413 414 public boolean init(Context context) { 415 Log.i(TAG, "Thermal Cooling manager init() called"); 416 417 mContext = context; 418 ThermalParser parser; 419 if (!ThermalManager.sIsOverlays) { 420 parser = new ThermalParser(ThermalManager.sThrottleFilePath); 421 } else { 422 parser = new ThermalParser(); 423 } 424 425 if (parser == null || !parser.parse()) { 426 Log.i(TAG, "thermal_throttle_config.xml parsing failed"); 427 return false; 428 } 429 430 // Set this sZoneCoolerBindMap to the DefaultProfile Map 431 ThermalManager.setCurBindMap(ThermalManager.DEFAULT_PROFILE_NAME); 432 433 // Register for thermal zone state changed notifications 434 IntentFilter filter = new IntentFilter(); 435 filter.addAction(ThermalManager.ACTION_THERMAL_ZONE_STATE_CHANGED); 436 mContext.registerReceiver(mThermalIntentReceiver, filter); 437 438 configureDynamicTurbo(); 439 return true; 440 } 441 442 private final class ProfileChangeReceiver extends BroadcastReceiver { 443 @Override 444 public void onReceive(Context context, Intent intent) { 445 String action = intent.getAction(); 446 if (action.equals(ThermalManager.ACTION_CHANGE_THERMAL_PROFILE)) { 447 String profName = intent.getStringExtra(ThermalManager.EXTRA_PROFILE); 448 if (profName != null) { 449 ThermalManager.changeThermalProfile(profName); 450 } 451 } 452 } 453 } 454 455 private void incrementCrticalZoneCount() { 456 synchronized(sCriticalZonesCountLock) { 457 mCriticalZonesCount++; 458 } 459 } 460 461 private final class ThermalZoneReceiver extends BroadcastReceiver { 462 @Override 463 public void onReceive(Context context, Intent intent) { 464 String zoneName = intent.getStringExtra(ThermalManager.EXTRA_NAME); 465 String profName = intent.getStringExtra(ThermalManager.EXTRA_PROFILE); 466 int thermZone = intent.getIntExtra(ThermalManager.EXTRA_ZONE, -1); 467 int thermState = intent.getIntExtra(ThermalManager.EXTRA_STATE, 0); 468 int thermEvent = intent.getIntExtra(ThermalManager.EXTRA_EVENT, 0); 469 int zoneTemp = intent.getIntExtra(ThermalManager.EXTRA_TEMP, 0); 470 471 // Assume 'Default' profile if there is no profile parameter 472 // as part of the intent. 473 if (profName == null) { 474 profName = ThermalManager.DEFAULT_PROFILE_NAME; 475 } 476 477 Log.i(TAG, "Received THERMAL INTENT:(ProfileName, ZoneName, State, EventType, Temp):" 478 + "(" + profName + ", " + zoneName + ", " + thermState + ", " 479 + ThermalZone.getEventTypeAsString(thermEvent) + ", " + zoneTemp + ")"); 480 481 Hashtable<Integer, ThermalManager.ZoneCoolerBindingInfo> mBindMap = 482 ThermalManager.getBindMap(profName); 483 if (mBindMap == null) { 484 Log.i(TAG, "mBindMap null inside ThermalZoneReceiver"); 485 return; 486 } 487 488 ThermalManager.ZoneCoolerBindingInfo zoneCoolerBindInfo = mBindMap.get(thermZone); 489 if (zoneCoolerBindInfo == null) { 490 Log.i(TAG, "zoneCoolerBindInfo null for zoneID" + thermZone); 491 return; 492 } 493 494 boolean flag = zoneCoolerBindInfo.getCriticalActionShutdown() == 1; 495 int lastState = zoneCoolerBindInfo.getLastState(); 496 if (thermState < lastState) { 497 ThermalManager.updateZoneCriticalPendingMap(thermZone, 498 ThermalManager.CRITICAL_FALSE); 499 } else if (thermState == lastState && flag) { 500 /* no telephony support, so (!isEmergencyCallOnGoing) is true */ 501 if (true) { 502 doShutdown(); 503 } else { 504 // increment the count of zones in critical state pending on shutdown 505 ThermalManager.updateZoneCriticalPendingMap(thermZone, 506 ThermalManager.CRITICAL_TRUE); 507 } 508 } 509 510 /* if THERMALOFF is the zone state, it is guaranteed that the zone has transitioned 511 from a higher state, due to a low event, to THERMALOFF.Hence take de-throttling action 512 corresponding to NORMAL */ 513 if (thermState == ThermalManager.THERMAL_STATE_OFF) { 514 thermState = ThermalManager.THERMAL_STATE_NORMAL; 515 } 516 handleThermalEvent(thermZone, thermEvent, thermState, zoneCoolerBindInfo); 517 } 518 } 519 520 private boolean loadCoolingDevice(ThermalCoolingDevice device) { 521 Class cls; 522 Method throttleMethod; 523 String classPath = device.getClassPath(); 524 525 if (classPath == null) { 526 Log.i(TAG, "ClassPath not found"); 527 return false; 528 } 529 530 if (classPath.equalsIgnoreCase("none") || classPath.equalsIgnoreCase("auto") 531 || classPath.equalsIgnoreCase("AppAgent")) { 532 Log.i(TAG, "ClassPath: none/auto/AppAgent"); 533 return true; 534 } 535 536 /* Load the cooling device class */ 537 try { 538 cls = Class.forName(classPath); 539 device.setDeviceClass(cls); 540 } catch (Throwable e) { 541 Log.i(TAG, "Unable to load class " + classPath); 542 return false; 543 } 544 545 /* Initialize the cooling device class */ 546 try { 547 Class partypes[] = new Class[3]; 548 partypes[0] = Context.class; 549 partypes[1] = String.class; 550 partypes[2] = ArrayList.class; 551 Method init = cls.getMethod("init", partypes); 552 Object arglist[] = new Object[3]; 553 arglist[0] = mContext; 554 arglist[1] = device.getThrottlePath(); 555 arglist[2] = device.getThrottleValuesList(); 556 init.invoke(cls, arglist); 557 } catch (NoSuchMethodException e) { 558 Log.i(TAG, "NoSuchMethodException caught in device class init: " + classPath); 559 } catch (SecurityException e) { 560 Log.i(TAG, "SecurityException caught in device class init: " + classPath); 561 } catch (IllegalAccessException e) { 562 Log.i(TAG, "IllegalAccessException caught in device class init: " + classPath); 563 } catch (IllegalArgumentException e) { 564 Log.i(TAG, "IllegalArgumentException caught in device class init: " + classPath); 565 } catch (ExceptionInInitializerError e) { 566 Log.i(TAG, "ExceptionInInitializerError caught in device class init: " + classPath); 567 } catch (InvocationTargetException e) { 568 Log.i(TAG, "InvocationTargetException caught in device class init: " + classPath); 569 } 570 571 /* Get the throttleDevice method from cooling device class */ 572 try { 573 Class partypes[] = new Class[1]; 574 partypes[0] = Integer.TYPE; 575 throttleMethod = cls.getMethod("throttleDevice", partypes); 576 device.setThrottleMethod(throttleMethod); 577 } catch (NoSuchMethodException e) { 578 Log.i(TAG, "NoSuchMethodException caught initializing throttle function"); 579 } catch (SecurityException e) { 580 Log.i(TAG, "SecurityException caught initializing throttle function"); 581 } 582 583 return true; 584 } 585 586 587 public void doShutdown() { 588 ThermalUtils.writeSysfs(THERMAL_SHUTDOWN_NOTIFY_PATH, 1); 589 /* We must avoid reboot after shutdown. */ 590 SystemProperties.set("sys.property_forcedshutdown", "1"); 591 Intent criticalIntent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); 592 criticalIntent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); 593 criticalIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 594 Log.i(TAG, "Thermal Service initiating shutdown"); 595 mContext.startActivity(criticalIntent); 596 } 597 598 public void registerProfChangeListener() { 599 IntentFilter profChangeIntentFilter = new IntentFilter(); 600 profChangeIntentFilter.addAction(ThermalManager.ACTION_CHANGE_THERMAL_PROFILE); 601 // TODO: add some permission (BRICK ??) to protect it from third party apps 602 mContext.registerReceiver(mProfChangeReceiver, profChangeIntentFilter); 603 mProfChangeListenerInitialized = true; 604 } 605 606 /* Method to handle the thermal event based on HIGH or LOW event */ 607 private void handleThermalEvent(int zoneId, int eventType, int thermalState, 608 ThermalManager.ZoneCoolerBindingInfo zoneCoolerBindInfo) { 609 ThermalCoolingDevice tDevice; 610 int deviceId; 611 int existingState, targetState; 612 int currThrottleMask, currDethrottleMask; 613 int index = 0; 614 615 if (zoneCoolerBindInfo.getCoolingDeviceInfoList() == null) 616 return; 617 618 for (ThermalManager.ZoneCoolerBindingInfo.CoolingDeviceInfo CdeviceInfo : 619 zoneCoolerBindInfo.getCoolingDeviceInfoList()) { 620 int coolingDeviceState = thermalState / 621 zoneCoolerBindInfo.getZoneToCoolDevBucketSizeIndex(index); 622 // cap it 623 coolingDeviceState = (coolingDeviceState > (CdeviceInfo.getCoolingDeviceStates() - 1)) 624 ? CdeviceInfo.getCoolingDeviceStates() - 1 : coolingDeviceState; 625 int finalThrottleState = coolingDeviceState * 626 zoneCoolerBindInfo.getCoolDevToThrottBucketSizeIndex(index); 627 // cap it 628 finalThrottleState = (finalThrottleState > (CdeviceInfo.getMaxThrottleStates() - 1)) 629 ? CdeviceInfo.getMaxThrottleStates() - 1 : finalThrottleState; 630 index++; 631 if (ThermalManager.THERMAL_HIGH_EVENT == eventType) { 632 ArrayList<Integer> throttleMaskList = CdeviceInfo.getThrottleMaskList(); 633 if (throttleMaskList == null) continue; 634 // cap to avoid out of bound exception 635 coolingDeviceState = (coolingDeviceState > throttleMaskList.size() - 1) 636 ? throttleMaskList.size() - 1 : coolingDeviceState; 637 currThrottleMask = throttleMaskList.get(coolingDeviceState); 638 deviceId = CdeviceInfo.getCoolingDeviceId(); 639 640 tDevice = ThermalManager.sCDevMap.get(deviceId); 641 if (tDevice == null) 642 continue; 643 644 if (currThrottleMask == ThermalManager.THROTTLE_MASK_ENABLE) { 645 existingState = tDevice.getThermalState(); 646 tDevice.updateZoneState(zoneId, finalThrottleState); 647 targetState = tDevice.getThermalState(); 648 649 /* Do not throttle if device is already in desired state. 650 * (We can save Sysfs write) 651 * */ 652 if (existingState != targetState) throttleDevice(deviceId, targetState); 653 654 } else { 655 // If throttle mask is not enabled, don't do anything here. 656 } 657 } 658 659 if (ThermalManager.THERMAL_LOW_EVENT == eventType) { 660 ArrayList<Integer> dethrottleMaskList = CdeviceInfo.getDeThrottleMaskList(); 661 if (dethrottleMaskList == null) continue; 662 // cap to avoid out of bound exception 663 coolingDeviceState = (coolingDeviceState > dethrottleMaskList.size() - 1) 664 ? dethrottleMaskList.size() - 1 : coolingDeviceState; 665 currDethrottleMask = dethrottleMaskList.get(coolingDeviceState); 666 deviceId = CdeviceInfo.getCoolingDeviceId(); 667 668 tDevice = ThermalManager.sCDevMap.get(deviceId); 669 if (tDevice == null) 670 continue; 671 672 existingState = tDevice.getThermalState(); 673 tDevice.updateZoneState(zoneId, finalThrottleState); 674 targetState = tDevice.getThermalState(); 675 676 /* Do not dethrottle if device is already in desired state. 677 * (We can save Sysfs write) */ 678 if ((existingState != targetState) && 679 (currDethrottleMask == ThermalManager.DETHROTTLE_MASK_ENABLE)) { 680 throttleDevice(deviceId, targetState); 681 } 682 } 683 } 684 685 } 686 687 /* 688 * defaultThrottleMethod is called for cooling devices for which an additional 689 * plugin file is not provided. Since the throttle path and the throttle values 690 * are known, we dont need an additional plugin to implement the policy. This info 691 * is provided via thermal_throttle_config file. If for a cooling device, 692 * Assumptions - 693 * 1. If CDeviceClassPath is 'auto' this triggers a call to defaultThrottleMethod(). 694 * if a false throttle path is provided, the write fails and function exits gracefully 695 * with a warning message. 696 * 2. If 'auto' mode is used for CDeviceClassPath, and no throttle values are provided, 697 * thermal state will be written. 698 * 3. If CDeviceThrottlePath is 'auto', then throttle path will be constrcuted. 699 * The Cooling device name should contain a subset string that matches the type for 700 * /sys/class/thermal/cooling_deviceX/type inorder to find the right index X 701 * 4. CDeviceThrottlePath is null no write operation will be done 702 **/ 703 private void defaultThrottleMethod(ThermalCoolingDevice cdev, int level) { 704 int finalValue; 705 String throttlePath = null; 706 707 if (cdev == null) return; 708 709 if (level < cdev.getNumThrottleValues() - 1) { 710 try { 711 ArrayList<Integer> values = cdev.getThrottleValuesList(); 712 if (values == null || values.size() == 0) { 713 finalValue = level; 714 } else { 715 finalValue = values.get(level); 716 } 717 718 throttlePath = cdev.getThrottlePath(); 719 if (throttlePath == null) { 720 Log.w(TAG, "throttle path is null"); 721 return; 722 } 723 724 if (!ThermalUtils.isFileExists(throttlePath)) { 725 Log.w(TAG, "invalid throttle path for cooling device:" + cdev.getDeviceName()); 726 return; 727 } 728 729 if (ThermalUtils.writeSysfs(throttlePath, finalValue) == -1) { 730 Log.w(TAG, "write to sysfs failed"); 731 } 732 } catch (IndexOutOfBoundsException e) { 733 Log.w(TAG, "IndexOutOfBoundsException caught in defaultThrottleMethod()"); 734 } 735 } 736 } 737 738 /* Method to throttle cooling device */ 739 private void throttleDevice(int coolingDevId, int throttleLevel) { 740 /* Retrieve the cooling device based on ID */ 741 ThermalCoolingDevice dev = ThermalManager.sCDevMap.get(coolingDevId); 742 if (dev != null) { 743 if (dev.getClassPath() != null && dev.getClassPath().equalsIgnoreCase("auto")) { 744 defaultThrottleMethod(dev, throttleLevel); 745 } else { 746 Class c = dev.getDeviceClass(); 747 Method throt = dev.getThrottleMethod(); 748 if (throt == null) 749 return; 750 Object arglist[] = new Object[1]; 751 arglist[0] = new Integer(throttleLevel); 752 753 // Invoke the throttle method passing the throttle level as parameter 754 try { 755 throt.invoke(c, arglist); 756 } catch (IllegalAccessException e) { 757 Log.i(TAG, "IllegalAccessException caught throttleDevice() "); 758 } catch (IllegalArgumentException e) { 759 Log.i(TAG, "IllegalArgumentException caught throttleDevice() "); 760 } catch (ExceptionInInitializerError e) { 761 Log.i(TAG, "ExceptionInInitializerError caught throttleDevice() "); 762 } catch (SecurityException e) { 763 Log.i(TAG, "SecurityException caught throttleDevice() "); 764 } catch (InvocationTargetException e) { 765 Log.i(TAG, "InvocationTargetException caught throttleDevice() "); 766 } 767 } 768 } else { 769 Log.i(TAG, "throttleDevice: Unable to retrieve cooling device " + coolingDevId); 770 } 771 } 772 773 public void unregisterReceivers() { 774 if (mContext != null) { 775 mContext.unregisterReceiver(mThermalIntentReceiver); 776 // During Thermal Service init, when parsing fails, we 777 // unregister all receivers here. mProfChangeReceiver 778 // might not have been initialized at that time because 779 // we initialize this only after starting the Default profile. 780 if (mProfChangeListenerInitialized) { 781 mContext.unregisterReceiver(mProfChangeReceiver); 782 } 783 } 784 } 785 } 786