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 
ThermalParser(String fname)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 
ThermalParser()137         ThermalParser() {
138             mParser = mContext.getResources().
139                     getXml(ThermalManager.sThrottleFileXmlId);
140         }
141 
parse()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 
processMetaTag(String tagName, MetaTag tagId)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         }
processStartElement(String name)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 
processEndElement(String name)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 
configureDynamicTurbo()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 
init(Context context)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
onReceive(Context context, Intent intent)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 
incrementCrticalZoneCount()455     private void incrementCrticalZoneCount() {
456         synchronized(sCriticalZonesCountLock) {
457             mCriticalZonesCount++;
458         }
459     }
460 
461     private final class ThermalZoneReceiver extends BroadcastReceiver {
462         @Override
onReceive(Context context, Intent intent)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 
loadCoolingDevice(ThermalCoolingDevice device)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 
doShutdown()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 
registerProfChangeListener()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 */
handleThermalEvent(int zoneId, int eventType, int thermalState, ThermalManager.ZoneCoolerBindingInfo zoneCoolerBindInfo)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      **/
defaultThrottleMethod(ThermalCoolingDevice cdev, int level)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 */
throttleDevice(int coolingDevId, int throttleLevel)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 
unregisterReceivers()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