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
17package com.intel.thermal;
18
19import org.xmlpull.v1.XmlPullParser;
20import org.xmlpull.v1.XmlPullParserException;
21import org.xmlpull.v1.XmlPullParserFactory;
22
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.os.SystemProperties;
28import android.util.Log;
29
30import java.io.FileNotFoundException;
31import java.io.FileReader;
32import java.io.IOException;
33import java.lang.reflect.InvocationTargetException;
34import java.lang.reflect.Method;
35import java.util.ArrayList;
36import 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 */
45public 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