| /* |
| * Copyright 2014 Intel Corporation All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.intel.thermal; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| import org.xmlpull.v1.XmlPullParserFactory; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.os.SystemProperties; |
| import android.util.Log; |
| |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Hashtable; |
| |
| /** |
| * The ThermalCooling class parses the thermal_throttle_config.xml. This class |
| * receives Thermal Intents and takes appropriate actions based on the policies |
| * configured in the xml file. |
| * |
| * @hide |
| */ |
| public class ThermalCooling { |
| private static final String TAG = "ThermalCooling"; |
| private static final String THERMAL_SHUTDOWN_NOTIFY_PATH = |
| "/sys/module/intel_mid_osip/parameters/force_shutdown_occured"; |
| |
| private Context mContext; |
| |
| // count to keep track of zones in critical state, waiting for shutdown |
| private int mCriticalZonesCount = 0; |
| private static final Object sCriticalZonesCountLock = new Object(); |
| |
| private ThermalZoneReceiver mThermalIntentReceiver = new ThermalZoneReceiver(); |
| private ProfileChangeReceiver mProfChangeReceiver = new ProfileChangeReceiver(); |
| private boolean mProfChangeListenerInitialized = false; |
| /** |
| * This is the parser class which parses the thermal_throttle_config.xml |
| * file. |
| */ |
| protected enum MetaTag { |
| ENUM_THROTTLEVALUES, |
| ENUM_THROTTLEMASK, |
| ENUM_DETHROTTLEMASK, |
| ENUM_UNKNOWN |
| } |
| |
| public class ThermalParser { |
| private static final String THERMAL_THROTTLE_CONFIG = "thermalthrottleconfig"; |
| |
| private static final String CDEVINFO = "ContributingDeviceInfo"; |
| |
| private static final String ZONETHROTINFO = "ZoneThrottleInfo"; |
| |
| private static final String COOLINGDEVICEINFO = "CoolingDeviceInfo"; |
| |
| private static final String THROTTLEMASK = "ThrottleDeviceMask"; |
| |
| private static final String DETHROTTLEMASK = "DethrottleDeviceMask"; |
| |
| private static final String THROTTLEVALUES = "ThrottleValues"; |
| |
| private static final String COOLINGDEVICESTATES = "CoolingDeviceStates"; |
| |
| private static final String PROFILE = "Profile"; |
| |
| private ArrayList<Integer> mTempMaskList; |
| |
| private ArrayList<Integer> mTempThrottleValuesList;; |
| |
| private boolean done = false; |
| |
| XmlPullParserFactory mFactory; |
| |
| XmlPullParser mParser; |
| |
| ThermalCoolingDevice mDevice = null; |
| |
| /* Hashtable of (ZoneID and ZoneCoolerBindingInfo object) */ |
| Hashtable<Integer, ThermalManager.ZoneCoolerBindingInfo> mZoneCoolerBindMap = null; |
| String mCurProfileName = ThermalManager.DEFAULT_PROFILE_NAME; |
| int mNumProfiles = 0; |
| |
| ThermalManager.ZoneCoolerBindingInfo mZone = null; |
| |
| FileReader mInputStream = null; |
| |
| ThermalParser(String fname) { |
| try { |
| mFactory = XmlPullParserFactory.newInstance( |
| System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); |
| mFactory.setNamespaceAware(true); |
| mParser = mFactory.newPullParser(); |
| } catch (XmlPullParserException xppe) { |
| Log.e(TAG, "mParser NewInstance Exception"); |
| } |
| |
| try { |
| mInputStream = new FileReader(fname); |
| if (mInputStream == null) |
| return; |
| if (mParser != null) { |
| mParser.setInput(mInputStream); |
| } |
| mDevice = null; |
| mZone = null; |
| } catch (XmlPullParserException xppe) { |
| Log.e(TAG, "mParser setInput XmlPullParserException"); |
| } catch (FileNotFoundException e) { |
| Log.e(TAG, "mParser setInput FileNotFoundException"); |
| } |
| |
| } |
| |
| ThermalParser() { |
| mParser = mContext.getResources(). |
| getXml(ThermalManager.sThrottleFileXmlId); |
| } |
| |
| public boolean parse() { |
| if (ThermalManager.sIsOverlays == false && mInputStream == null) return false; |
| /* if mParser is null, close any open stream before exiting */ |
| if (mParser == null) { |
| try { |
| if (mInputStream != null) { |
| mInputStream.close(); |
| } |
| } catch (IOException e) { |
| Log.i(TAG, "IOException caught in parse() function"); |
| } |
| return false; |
| } |
| |
| boolean ret = true; |
| MetaTag tag = MetaTag.ENUM_UNKNOWN; |
| try { |
| int mEventType = mParser.getEventType(); |
| while (mEventType != XmlPullParser.END_DOCUMENT && !done) { |
| switch (mEventType) { |
| case XmlPullParser.START_DOCUMENT: |
| Log.i(TAG, "StartDocument"); |
| break; |
| case XmlPullParser.START_TAG: |
| String tagName = mParser.getName(); |
| boolean isMetaTag = false; |
| if (tagName != null && tagName.equalsIgnoreCase(THROTTLEVALUES)) { |
| tag = MetaTag.ENUM_THROTTLEVALUES; |
| isMetaTag = true; |
| } else if (tagName != null && tagName.equalsIgnoreCase(THROTTLEMASK)) { |
| tag = MetaTag.ENUM_THROTTLEMASK; |
| isMetaTag = true; |
| } else if (tagName != null |
| && tagName.equalsIgnoreCase(DETHROTTLEMASK)) { |
| tag = MetaTag.ENUM_DETHROTTLEMASK; |
| isMetaTag = true; |
| } |
| if (isMetaTag) { |
| ret = processMetaTag(tagName, tag); |
| } else { |
| ret = processStartElement(tagName); |
| } |
| if (!ret) { |
| if (mInputStream != null) mInputStream.close(); |
| return false; |
| } |
| break; |
| case XmlPullParser.END_TAG: |
| processEndElement(mParser.getName()); |
| break; |
| } |
| mEventType = mParser.next(); |
| } |
| } catch (XmlPullParserException xppe) { |
| Log.i(TAG, "XmlPullParserException caught in parse():" + xppe.getMessage()); |
| ret = false; |
| } catch (IOException e) { |
| Log.i(TAG, "IOException caught in parse():" + e.getMessage()); |
| ret = false; |
| } finally { |
| try { |
| // end of parsing, close the stream |
| // close is moved here, since if there is an exception |
| // while parsing doc, input stream needs to be closed |
| if (mInputStream != null) { |
| mInputStream.close(); |
| } |
| } catch (IOException e) { |
| Log.i(TAG, "IOException caught in parse() function"); |
| ret = false; |
| } |
| return ret; |
| } |
| } |
| |
| public boolean processMetaTag(String tagName, MetaTag tagId) { |
| if (mParser == null || tagName == null) return false; |
| ArrayList<Integer> tempList = new ArrayList<Integer>(); |
| try { |
| int eventType = mParser.next(); |
| while (true) { |
| if (eventType == XmlPullParser.START_TAG) { |
| tempList.add(Integer.parseInt(mParser.nextText())); |
| } else if (eventType == XmlPullParser.END_TAG && |
| mParser.getName().equalsIgnoreCase(tagName)) { |
| break; |
| } |
| eventType = mParser.next(); |
| } |
| } catch (XmlPullParserException xppe) { |
| Log.e(TAG, "XmlPullParserException:" + xppe.getMessage()); |
| return false; |
| } catch (IOException ioe) { |
| Log.e(TAG, "IOException:" + ioe.getMessage()); |
| return false; |
| } |
| |
| switch(tagId) { |
| case ENUM_THROTTLEVALUES: |
| if (mDevice == null) { |
| return false; |
| } else { |
| // add throttle value for TCRITICAL (same as last value) |
| tempList.add(tempList.get(tempList.size() - 1)); |
| mDevice.setThrottleValuesList(tempList); |
| } |
| break; |
| case ENUM_THROTTLEMASK: |
| if (mZone == null || mZone.getLastCoolingDeviceInstance() == null) { |
| return false; |
| } else { |
| // Always throttle at CRITICAL state (last state) |
| tempList.add(1); |
| mZone.getLastCoolingDeviceInstance().setThrottleMaskList(tempList); |
| } |
| break; |
| case ENUM_DETHROTTLEMASK: |
| if (mZone == null || mZone.getLastCoolingDeviceInstance() == null) { |
| return false; |
| } else { |
| // Dethrottling at CRITICAL state (last state) is dontcare condition |
| tempList.add(0); |
| mZone.getLastCoolingDeviceInstance().setDeThrottleMaskList(tempList); |
| } |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| boolean processStartElement(String name) { |
| if (name == null) |
| return false; |
| boolean ret = true; |
| try { |
| if (name.equalsIgnoreCase(CDEVINFO)) { |
| if (mDevice == null) |
| mDevice = new ThermalCoolingDevice(); |
| } else if (name.equalsIgnoreCase(ZONETHROTINFO)) { |
| if (mZone == null) { |
| mZone = new ThermalManager.ZoneCoolerBindingInfo(); |
| } |
| if (mZoneCoolerBindMap == null) { |
| mZoneCoolerBindMap = new Hashtable<Integer, |
| ThermalManager.ZoneCoolerBindingInfo>(); |
| } |
| } else if (name.equalsIgnoreCase(PROFILE)) { |
| mNumProfiles++; |
| if (mZoneCoolerBindMap == null) { |
| mZoneCoolerBindMap = new Hashtable<Integer, |
| ThermalManager.ZoneCoolerBindingInfo>(); |
| } |
| } else if (name.equalsIgnoreCase(COOLINGDEVICEINFO) && mZone != null) { |
| if (mZone.getCoolingDeviceInfoList() == null) { |
| mZone.initializeCoolingDeviceInfoList(); |
| } |
| mZone.createNewCoolingDeviceInstance(); |
| } else { |
| // Retrieve zone and cooling device mapping |
| if (name.equalsIgnoreCase("ZoneID") && mZone != null) { |
| mZone.setZoneID(Integer.parseInt(mParser.nextText())); |
| } else if (name.equalsIgnoreCase("CriticalShutDown") && mZone != null) { |
| mZone.setCriticalActionShutdown(Integer.parseInt(mParser.nextText())); |
| } else if (name.equalsIgnoreCase(THROTTLEMASK) && mZone != null) { |
| mTempMaskList = new ArrayList<Integer>(); |
| } else if (name.equalsIgnoreCase(DETHROTTLEMASK) && mZone != null) { |
| mTempMaskList = new ArrayList<Integer>(); |
| } else if (name.equalsIgnoreCase("CoolingDevId") && mZone != null) { |
| mZone.getLastCoolingDeviceInstance().setCoolingDeviceId( |
| Integer.parseInt(mParser.nextText())); |
| } else if (name.equalsIgnoreCase(COOLINGDEVICESTATES) && mZone != null) { |
| // Increase cooling device states by 1, required for CRITICAL state |
| mZone.getLastCoolingDeviceInstance().setCoolingDeviceStates( |
| Integer.parseInt(mParser.nextText()) + 1); |
| } |
| // Retrieve cooling device information |
| if (name.equalsIgnoreCase("CDeviceName") && mDevice != null) { |
| mDevice.setDeviceName(mParser.nextText()); |
| } else if (name.equalsIgnoreCase("CDeviceID") && mDevice != null) { |
| mDevice.setDeviceId(Integer.parseInt(mParser.nextText())); |
| } else if (name.equalsIgnoreCase("CDeviceClassPath") && mDevice != null) { |
| mDevice.setClassPath(mParser.nextText()); |
| } else if (name.equalsIgnoreCase("CDeviceThrottlePath") && mDevice != null) { |
| mDevice.setThrottlePath(mParser.nextText()); |
| } else if (name.equalsIgnoreCase("Name")) { |
| mCurProfileName = mParser.nextText(); |
| } |
| } |
| } catch (XmlPullParserException e) { |
| Log.i(TAG, "XmlPullParserException caught in processStartElement()"); |
| ret = false; |
| } catch (IOException e) { |
| Log.i(TAG, "IOException caught in processStartElement()"); |
| ret = false; |
| } finally { |
| return ret; |
| } |
| } |
| |
| void processEndElement(String name) { |
| if (name == null) |
| return; |
| if (name.equalsIgnoreCase(CDEVINFO) && mDevice != null) { |
| // if cooling dev suports less then DEFAULT throttle values donot add to map. |
| if (mDevice.getNumThrottleValues() < ThermalManager.DEFAULT_NUM_THROTTLE_VALUES) { |
| Log.i(TAG, "cooling dev:" + mDevice.getDeviceName() |
| + " deactivated! throttle values < " |
| + ThermalManager.DEFAULT_NUM_THROTTLE_VALUES); |
| mDevice = null; |
| return; |
| } |
| if (mDevice.getThrottlePath().equals("auto")) { |
| mDevice.setThrottlePath("auto"); |
| } |
| if (loadCoolingDevice(mDevice)) { |
| ThermalManager.sCDevMap.put(mDevice.getDeviceId(), mDevice); |
| } |
| mDevice = null; |
| } else if (name.equalsIgnoreCase(ZONETHROTINFO) && mZone != null) { |
| mZone.printAttributes(); |
| if (mZoneCoolerBindMap != null) { |
| mZoneCoolerBindMap.put(mZone.getZoneID(), mZone); |
| } |
| mZone = null; |
| } else if (name.equalsIgnoreCase(PROFILE)) { |
| if (mZoneCoolerBindMap != null) { |
| ThermalManager.sProfileBindMap.put(mCurProfileName, mZoneCoolerBindMap); |
| mZoneCoolerBindMap = new Hashtable<Integer, |
| ThermalManager.ZoneCoolerBindingInfo>(); |
| } |
| } else if (name.equalsIgnoreCase(THERMAL_THROTTLE_CONFIG)) { |
| Log.i(TAG, "Parsing Finished.."); |
| // This indicates we have not seen any <Profile> tag. |
| // Consider it as if we have only one 'Default' Profile. |
| if (mNumProfiles == 0 && mZoneCoolerBindMap != null) { |
| ThermalManager.sProfileBindMap.put(mCurProfileName, mZoneCoolerBindMap); |
| } |
| done = true; |
| } else if (name.equalsIgnoreCase(COOLINGDEVICEINFO) && mZone != null) { |
| ThermalManager.ZoneCoolerBindingInfo.CoolingDeviceInfo cDevInfo; |
| cDevInfo = mZone.getLastCoolingDeviceInstance(); |
| if (cDevInfo != null) { |
| ThermalCoolingDevice cDev = ThermalManager.sCDevMap |
| .get(cDevInfo.getCoolingDeviceId()); |
| if (cDev == null) return; |
| int cds = cDevInfo.getCoolingDeviceStates(); |
| // check the CDS against the number of throttle values exposed. |
| // If exceeds, cap it. |
| if (cds > cDev.getNumThrottleValues()) { |
| cDevInfo.setCoolingDeviceStates(cDev.getNumThrottleValues()); |
| Log.i(TAG, "capping cdevid: " + cDevInfo.getCoolingDeviceId() |
| + " to " + cDev.getNumThrottleValues() + " states"); |
| } |
| if (cDevInfo.checkMaskList(cDev.getNumThrottleValues())) { |
| // add only active cooling devices to list |
| mZone.addCoolingDeviceToList(cDevInfo); |
| } |
| } |
| } |
| } |
| } |
| |
| private void configureDynamicTurbo() { |
| // Disable Dynamic Turbo based on the system property |
| int indx = ThermalUtils.getCoolingDeviceIndexContains("SoC"); |
| if (indx != -1 && !ThermalManager.sIsDynamicTurboEnabled) { |
| String path = ThermalManager.sCoolingDeviceBasePath + indx |
| + ThermalManager.sCoolingDeviceState; |
| ThermalUtils.writeSysfs(path, ThermalManager.DISABLE_DYNAMIC_TURBO); |
| } |
| } |
| |
| public boolean init(Context context) { |
| Log.i(TAG, "Thermal Cooling manager init() called"); |
| |
| mContext = context; |
| ThermalParser parser; |
| if (!ThermalManager.sIsOverlays) { |
| parser = new ThermalParser(ThermalManager.sThrottleFilePath); |
| } else { |
| parser = new ThermalParser(); |
| } |
| |
| if (parser == null || !parser.parse()) { |
| Log.i(TAG, "thermal_throttle_config.xml parsing failed"); |
| return false; |
| } |
| |
| // Set this sZoneCoolerBindMap to the DefaultProfile Map |
| ThermalManager.setCurBindMap(ThermalManager.DEFAULT_PROFILE_NAME); |
| |
| // Register for thermal zone state changed notifications |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(ThermalManager.ACTION_THERMAL_ZONE_STATE_CHANGED); |
| mContext.registerReceiver(mThermalIntentReceiver, filter); |
| |
| configureDynamicTurbo(); |
| return true; |
| } |
| |
| private final class ProfileChangeReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String action = intent.getAction(); |
| if (action.equals(ThermalManager.ACTION_CHANGE_THERMAL_PROFILE)) { |
| String profName = intent.getStringExtra(ThermalManager.EXTRA_PROFILE); |
| if (profName != null) { |
| ThermalManager.changeThermalProfile(profName); |
| } |
| } |
| } |
| } |
| |
| private void incrementCrticalZoneCount() { |
| synchronized(sCriticalZonesCountLock) { |
| mCriticalZonesCount++; |
| } |
| } |
| |
| private final class ThermalZoneReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String zoneName = intent.getStringExtra(ThermalManager.EXTRA_NAME); |
| String profName = intent.getStringExtra(ThermalManager.EXTRA_PROFILE); |
| int thermZone = intent.getIntExtra(ThermalManager.EXTRA_ZONE, -1); |
| int thermState = intent.getIntExtra(ThermalManager.EXTRA_STATE, 0); |
| int thermEvent = intent.getIntExtra(ThermalManager.EXTRA_EVENT, 0); |
| int zoneTemp = intent.getIntExtra(ThermalManager.EXTRA_TEMP, 0); |
| |
| // Assume 'Default' profile if there is no profile parameter |
| // as part of the intent. |
| if (profName == null) { |
| profName = ThermalManager.DEFAULT_PROFILE_NAME; |
| } |
| |
| Log.i(TAG, "Received THERMAL INTENT:(ProfileName, ZoneName, State, EventType, Temp):" |
| + "(" + profName + ", " + zoneName + ", " + thermState + ", " |
| + ThermalZone.getEventTypeAsString(thermEvent) + ", " + zoneTemp + ")"); |
| |
| Hashtable<Integer, ThermalManager.ZoneCoolerBindingInfo> mBindMap = |
| ThermalManager.getBindMap(profName); |
| if (mBindMap == null) { |
| Log.i(TAG, "mBindMap null inside ThermalZoneReceiver"); |
| return; |
| } |
| |
| ThermalManager.ZoneCoolerBindingInfo zoneCoolerBindInfo = mBindMap.get(thermZone); |
| if (zoneCoolerBindInfo == null) { |
| Log.i(TAG, "zoneCoolerBindInfo null for zoneID" + thermZone); |
| return; |
| } |
| |
| boolean flag = zoneCoolerBindInfo.getCriticalActionShutdown() == 1; |
| int lastState = zoneCoolerBindInfo.getLastState(); |
| if (thermState < lastState) { |
| ThermalManager.updateZoneCriticalPendingMap(thermZone, |
| ThermalManager.CRITICAL_FALSE); |
| } else if (thermState == lastState && flag) { |
| /* no telephony support, so (!isEmergencyCallOnGoing) is true */ |
| if (true) { |
| doShutdown(); |
| } else { |
| // increment the count of zones in critical state pending on shutdown |
| ThermalManager.updateZoneCriticalPendingMap(thermZone, |
| ThermalManager.CRITICAL_TRUE); |
| } |
| } |
| |
| /* if THERMALOFF is the zone state, it is guaranteed that the zone has transitioned |
| from a higher state, due to a low event, to THERMALOFF.Hence take de-throttling action |
| corresponding to NORMAL */ |
| if (thermState == ThermalManager.THERMAL_STATE_OFF) { |
| thermState = ThermalManager.THERMAL_STATE_NORMAL; |
| } |
| handleThermalEvent(thermZone, thermEvent, thermState, zoneCoolerBindInfo); |
| } |
| } |
| |
| private boolean loadCoolingDevice(ThermalCoolingDevice device) { |
| Class cls; |
| Method throttleMethod; |
| String classPath = device.getClassPath(); |
| |
| if (classPath == null) { |
| Log.i(TAG, "ClassPath not found"); |
| return false; |
| } |
| |
| if (classPath.equalsIgnoreCase("none") || classPath.equalsIgnoreCase("auto") |
| || classPath.equalsIgnoreCase("AppAgent")) { |
| Log.i(TAG, "ClassPath: none/auto/AppAgent"); |
| return true; |
| } |
| |
| /* Load the cooling device class */ |
| try { |
| cls = Class.forName(classPath); |
| device.setDeviceClass(cls); |
| } catch (Throwable e) { |
| Log.i(TAG, "Unable to load class " + classPath); |
| return false; |
| } |
| |
| /* Initialize the cooling device class */ |
| try { |
| Class partypes[] = new Class[3]; |
| partypes[0] = Context.class; |
| partypes[1] = String.class; |
| partypes[2] = ArrayList.class; |
| Method init = cls.getMethod("init", partypes); |
| Object arglist[] = new Object[3]; |
| arglist[0] = mContext; |
| arglist[1] = device.getThrottlePath(); |
| arglist[2] = device.getThrottleValuesList(); |
| init.invoke(cls, arglist); |
| } catch (NoSuchMethodException e) { |
| Log.i(TAG, "NoSuchMethodException caught in device class init: " + classPath); |
| } catch (SecurityException e) { |
| Log.i(TAG, "SecurityException caught in device class init: " + classPath); |
| } catch (IllegalAccessException e) { |
| Log.i(TAG, "IllegalAccessException caught in device class init: " + classPath); |
| } catch (IllegalArgumentException e) { |
| Log.i(TAG, "IllegalArgumentException caught in device class init: " + classPath); |
| } catch (ExceptionInInitializerError e) { |
| Log.i(TAG, "ExceptionInInitializerError caught in device class init: " + classPath); |
| } catch (InvocationTargetException e) { |
| Log.i(TAG, "InvocationTargetException caught in device class init: " + classPath); |
| } |
| |
| /* Get the throttleDevice method from cooling device class */ |
| try { |
| Class partypes[] = new Class[1]; |
| partypes[0] = Integer.TYPE; |
| throttleMethod = cls.getMethod("throttleDevice", partypes); |
| device.setThrottleMethod(throttleMethod); |
| } catch (NoSuchMethodException e) { |
| Log.i(TAG, "NoSuchMethodException caught initializing throttle function"); |
| } catch (SecurityException e) { |
| Log.i(TAG, "SecurityException caught initializing throttle function"); |
| } |
| |
| return true; |
| } |
| |
| |
| public void doShutdown() { |
| ThermalUtils.writeSysfs(THERMAL_SHUTDOWN_NOTIFY_PATH, 1); |
| /* We must avoid reboot after shutdown. */ |
| SystemProperties.set("sys.property_forcedshutdown", "1"); |
| Intent criticalIntent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); |
| criticalIntent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); |
| criticalIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| Log.i(TAG, "Thermal Service initiating shutdown"); |
| mContext.startActivity(criticalIntent); |
| } |
| |
| public void registerProfChangeListener() { |
| IntentFilter profChangeIntentFilter = new IntentFilter(); |
| profChangeIntentFilter.addAction(ThermalManager.ACTION_CHANGE_THERMAL_PROFILE); |
| // TODO: add some permission (BRICK ??) to protect it from third party apps |
| mContext.registerReceiver(mProfChangeReceiver, profChangeIntentFilter); |
| mProfChangeListenerInitialized = true; |
| } |
| |
| /* Method to handle the thermal event based on HIGH or LOW event */ |
| private void handleThermalEvent(int zoneId, int eventType, int thermalState, |
| ThermalManager.ZoneCoolerBindingInfo zoneCoolerBindInfo) { |
| ThermalCoolingDevice tDevice; |
| int deviceId; |
| int existingState, targetState; |
| int currThrottleMask, currDethrottleMask; |
| int index = 0; |
| |
| if (zoneCoolerBindInfo.getCoolingDeviceInfoList() == null) |
| return; |
| |
| for (ThermalManager.ZoneCoolerBindingInfo.CoolingDeviceInfo CdeviceInfo : |
| zoneCoolerBindInfo.getCoolingDeviceInfoList()) { |
| int coolingDeviceState = thermalState / |
| zoneCoolerBindInfo.getZoneToCoolDevBucketSizeIndex(index); |
| // cap it |
| coolingDeviceState = (coolingDeviceState > (CdeviceInfo.getCoolingDeviceStates() - 1)) |
| ? CdeviceInfo.getCoolingDeviceStates() - 1 : coolingDeviceState; |
| int finalThrottleState = coolingDeviceState * |
| zoneCoolerBindInfo.getCoolDevToThrottBucketSizeIndex(index); |
| // cap it |
| finalThrottleState = (finalThrottleState > (CdeviceInfo.getMaxThrottleStates() - 1)) |
| ? CdeviceInfo.getMaxThrottleStates() - 1 : finalThrottleState; |
| index++; |
| if (ThermalManager.THERMAL_HIGH_EVENT == eventType) { |
| ArrayList<Integer> throttleMaskList = CdeviceInfo.getThrottleMaskList(); |
| if (throttleMaskList == null) continue; |
| // cap to avoid out of bound exception |
| coolingDeviceState = (coolingDeviceState > throttleMaskList.size() - 1) |
| ? throttleMaskList.size() - 1 : coolingDeviceState; |
| currThrottleMask = throttleMaskList.get(coolingDeviceState); |
| deviceId = CdeviceInfo.getCoolingDeviceId(); |
| |
| tDevice = ThermalManager.sCDevMap.get(deviceId); |
| if (tDevice == null) |
| continue; |
| |
| if (currThrottleMask == ThermalManager.THROTTLE_MASK_ENABLE) { |
| existingState = tDevice.getThermalState(); |
| tDevice.updateZoneState(zoneId, finalThrottleState); |
| targetState = tDevice.getThermalState(); |
| |
| /* Do not throttle if device is already in desired state. |
| * (We can save Sysfs write) |
| * */ |
| if (existingState != targetState) throttleDevice(deviceId, targetState); |
| |
| } else { |
| // If throttle mask is not enabled, don't do anything here. |
| } |
| } |
| |
| if (ThermalManager.THERMAL_LOW_EVENT == eventType) { |
| ArrayList<Integer> dethrottleMaskList = CdeviceInfo.getDeThrottleMaskList(); |
| if (dethrottleMaskList == null) continue; |
| // cap to avoid out of bound exception |
| coolingDeviceState = (coolingDeviceState > dethrottleMaskList.size() - 1) |
| ? dethrottleMaskList.size() - 1 : coolingDeviceState; |
| currDethrottleMask = dethrottleMaskList.get(coolingDeviceState); |
| deviceId = CdeviceInfo.getCoolingDeviceId(); |
| |
| tDevice = ThermalManager.sCDevMap.get(deviceId); |
| if (tDevice == null) |
| continue; |
| |
| existingState = tDevice.getThermalState(); |
| tDevice.updateZoneState(zoneId, finalThrottleState); |
| targetState = tDevice.getThermalState(); |
| |
| /* Do not dethrottle if device is already in desired state. |
| * (We can save Sysfs write) */ |
| if ((existingState != targetState) && |
| (currDethrottleMask == ThermalManager.DETHROTTLE_MASK_ENABLE)) { |
| throttleDevice(deviceId, targetState); |
| } |
| } |
| } |
| |
| } |
| |
| /* |
| * defaultThrottleMethod is called for cooling devices for which an additional |
| * plugin file is not provided. Since the throttle path and the throttle values |
| * are known, we dont need an additional plugin to implement the policy. This info |
| * is provided via thermal_throttle_config file. If for a cooling device, |
| * Assumptions - |
| * 1. If CDeviceClassPath is 'auto' this triggers a call to defaultThrottleMethod(). |
| * if a false throttle path is provided, the write fails and function exits gracefully |
| * with a warning message. |
| * 2. If 'auto' mode is used for CDeviceClassPath, and no throttle values are provided, |
| * thermal state will be written. |
| * 3. If CDeviceThrottlePath is 'auto', then throttle path will be constrcuted. |
| * The Cooling device name should contain a subset string that matches the type for |
| * /sys/class/thermal/cooling_deviceX/type inorder to find the right index X |
| * 4. CDeviceThrottlePath is null no write operation will be done |
| **/ |
| private void defaultThrottleMethod(ThermalCoolingDevice cdev, int level) { |
| int finalValue; |
| String throttlePath = null; |
| |
| if (cdev == null) return; |
| |
| if (level < cdev.getNumThrottleValues() - 1) { |
| try { |
| ArrayList<Integer> values = cdev.getThrottleValuesList(); |
| if (values == null || values.size() == 0) { |
| finalValue = level; |
| } else { |
| finalValue = values.get(level); |
| } |
| |
| throttlePath = cdev.getThrottlePath(); |
| if (throttlePath == null) { |
| Log.w(TAG, "throttle path is null"); |
| return; |
| } |
| |
| if (!ThermalUtils.isFileExists(throttlePath)) { |
| Log.w(TAG, "invalid throttle path for cooling device:" + cdev.getDeviceName()); |
| return; |
| } |
| |
| if (ThermalUtils.writeSysfs(throttlePath, finalValue) == -1) { |
| Log.w(TAG, "write to sysfs failed"); |
| } |
| } catch (IndexOutOfBoundsException e) { |
| Log.w(TAG, "IndexOutOfBoundsException caught in defaultThrottleMethod()"); |
| } |
| } |
| } |
| |
| /* Method to throttle cooling device */ |
| private void throttleDevice(int coolingDevId, int throttleLevel) { |
| /* Retrieve the cooling device based on ID */ |
| ThermalCoolingDevice dev = ThermalManager.sCDevMap.get(coolingDevId); |
| if (dev != null) { |
| if (dev.getClassPath() != null && dev.getClassPath().equalsIgnoreCase("auto")) { |
| defaultThrottleMethod(dev, throttleLevel); |
| } else { |
| Class c = dev.getDeviceClass(); |
| Method throt = dev.getThrottleMethod(); |
| if (throt == null) |
| return; |
| Object arglist[] = new Object[1]; |
| arglist[0] = new Integer(throttleLevel); |
| |
| // Invoke the throttle method passing the throttle level as parameter |
| try { |
| throt.invoke(c, arglist); |
| } catch (IllegalAccessException e) { |
| Log.i(TAG, "IllegalAccessException caught throttleDevice() "); |
| } catch (IllegalArgumentException e) { |
| Log.i(TAG, "IllegalArgumentException caught throttleDevice() "); |
| } catch (ExceptionInInitializerError e) { |
| Log.i(TAG, "ExceptionInInitializerError caught throttleDevice() "); |
| } catch (SecurityException e) { |
| Log.i(TAG, "SecurityException caught throttleDevice() "); |
| } catch (InvocationTargetException e) { |
| Log.i(TAG, "InvocationTargetException caught throttleDevice() "); |
| } |
| } |
| } else { |
| Log.i(TAG, "throttleDevice: Unable to retrieve cooling device " + coolingDevId); |
| } |
| } |
| |
| public void unregisterReceivers() { |
| if (mContext != null) { |
| mContext.unregisterReceiver(mThermalIntentReceiver); |
| // During Thermal Service init, when parsing fails, we |
| // unregister all receivers here. mProfChangeReceiver |
| // might not have been initialized at that time because |
| // we initialize this only after starting the Default profile. |
| if (mProfChangeListenerInitialized) { |
| mContext.unregisterReceiver(mProfChangeReceiver); |
| } |
| } |
| } |
| } |