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 android.os.UEventObserver;
20import android.util.Log;
21
22import java.lang.Math;
23import java.util.ArrayList;
24import java.util.Arrays;
25
26/**
27 * The VirtualThermalZone class extends the ThermalZone class, with a default
28 * implementation of the isZoneStateChanged() method. This computes the
29 * zone state by computing the equation, which can be linear / higher order implementation
30 * @hide
31 */
32public class VirtualThermalZone extends ThermalZone {
33
34    private static final String TAG = "VirtualThermalZone";
35    private String mEmulTempPath;
36    private ThermalZoneMonitor mTzm = null;
37
38    public void setEmulTempPath(String path) {
39        mEmulTempPath = path;
40    }
41
42    public String getEmulTempPath() {
43        return mEmulTempPath;
44    }
45
46    public VirtualThermalZone() {
47        super();
48    }
49
50    // overridden to start UEvent observer only for Virtual zone
51    public void startEmulTempObserver() {
52        if (!getEmulTempFlag()) {
53            return;
54        }
55        int indx = ThermalUtils.getThermalZoneIndex(getZoneName());
56        if (indx == -1) {
57            Log.i(TAG, "Could not obtain emul_temp sysfs node for " + getZoneName());
58            return;
59        }
60        String uEventDevPath = ThermalManager.sUEventDevPath + indx;
61        setEmulTempPath(ThermalManager.sSysfsSensorBasePath + indx + "/emul_temp");
62        mEmulTempObserver.startObserving(uEventDevPath);
63    }
64
65    public void unregisterReceiver() {
66        super.unregisterReceiver();
67        if (getEmulTempFlag()) {
68            mEmulTempObserver.stopObserving();
69        }
70    }
71
72    public void startMonitoring() {
73        mTzm = new ThermalZoneMonitor(this);
74    }
75
76    public void stopMonitoring() {
77        if (mTzm != null) {
78            mTzm.stopMonitor();
79        }
80    }
81
82    // override fucntion
83    public void calibrateThresholds() {
84        ThermalSensor ts = getThermalSensorList().get(0);
85        ThermalSensorAttrib sa = mThermalSensorsAttribMap.get(ts.getSensorName());
86        if (sa == null) {
87            return;
88        }
89        Integer weights[] = sa.getWeights();
90        int m = weights[0];
91        int c = getOffset();
92
93        if (m == 0) return;
94        for (int i = 0; i < mZoneTempThresholdsRaw.length; i++) {
95            // We do not want to convert '0'. Let it represent 0 C.
96            if (mZoneTempThresholdsRaw[i] == 0) continue;
97            // Get raw systherm temperature: y=mx+c <--> x=(y-c)/m
98            mZoneTempThresholds[i] = ((mZoneTempThresholdsRaw[i] - c) * 1000) / m;
99        }
100        Log.i(TAG, "calibrateThresholds[]: " + Arrays.toString(mZoneTempThresholds));
101    }
102
103    private int calculateZoneTemp() {
104        int curZoneTemp = 0;
105        int weightedTemp;
106        ArrayList<ThermalSensor> list = getThermalSensorList();
107
108        // Check if the SensorList is sane and usable
109        if (list == null || list.get(0) == null) {
110            return ThermalManager.INVALID_TEMP;
111        }
112
113        if (isUEventSupported()) {
114            // for uevent based monitoring only first sensor used
115            ThermalSensor ts = list.get(0);
116            weightedTemp = getWeightedTemp(ts, ts.readSensorTemp());
117            return weightedTemp == ThermalManager.INVALID_TEMP
118                    ? ThermalManager.INVALID_TEMP : weightedTemp + getOffset();
119        }
120
121        // Polling mode
122        for (ThermalSensor ts : list) {
123            if (ts != null && ts.getSensorActiveStatus()) {
124                weightedTemp = getWeightedTemp(ts, ts.readSensorTemp());
125                if (weightedTemp == ThermalManager.INVALID_TEMP) {
126                    return ThermalManager.INVALID_TEMP;
127                }
128                curZoneTemp += weightedTemp;
129            }
130        }
131        return curZoneTemp + getOffset();
132    }
133
134    private UEventObserver mEmulTempObserver = new UEventObserver() {
135        @Override
136        public void onUEvent(UEventObserver.UEvent event) {
137            String type = event.get("EVENT");
138            if (type == null || Integer.parseInt(type) != ThermalManager.THERMAL_EMUL_TEMP_EVENT) {
139                Log.i(TAG, "EventType does not match");
140                return;
141            }
142
143            if (!getZoneName().equals(event.get("NAME"))) {
144                Log.i(TAG, "ZoneName does not match");
145                return;
146            }
147
148            int temp = calculateZoneTemp();
149            if (temp == ThermalManager.INVALID_TEMP) {
150                Log.i(TAG, "Obtained INVALID_TEMP[0xDEADBEEF]");
151                return;
152            }
153
154            String path = getEmulTempPath();
155            if (path == null) {
156                Log.i(TAG, "EmulTempPath is null");
157                return;
158            }
159
160            int ret = ThermalUtils.writeSysfs(path, temp);
161            if (ret == -1) {
162                Log.i(TAG, "Writing into emul_temp sysfs failed");
163            }
164        }
165    };
166
167    // override function
168    public boolean updateZoneTemp() {
169        int curZoneTemp = ThermalManager.INVALID_TEMP;
170        int rawSensorTemp, sensorTemp;
171        int weightedTemp;
172        boolean flag = false;
173
174        if (isUEventSupported()) {
175            // In UEvent mode, the obtained temperature is the zone temperature
176            return true;
177        } else {
178            for (int i = 0; i < mThermalSensors.size(); i++) {
179                if (mThermalSensors.get(i) != null
180                        && mThermalSensors.get(i).getSensorActiveStatus()) {
181                    if (flag == false) {
182                        // one time initialization of zone temp
183                        curZoneTemp = 0;
184                        flag = true;
185                    }
186                    weightedTemp = getWeightedTemp(mThermalSensors.get(i));
187                    if (weightedTemp != ThermalManager.INVALID_TEMP) {
188                        curZoneTemp += weightedTemp;
189                    }
190                }
191            }
192        }
193
194        if (curZoneTemp != ThermalManager.INVALID_TEMP) {
195            curZoneTemp += getOffset();
196            setZoneTemp(curZoneTemp);
197            if (getMovingAverageFlag() && !isUEventSupported()) {
198                // only for polling mode apply moving average on predicted zone temp
199                setZoneTemp(movingAverageTemp());
200            }
201            return true;
202        }
203
204        return false;
205    }
206
207    private int getWeightedTemp(ThermalSensor ts) {
208        return getWeightedTemp(ts, ts.getCurrTemp());
209    }
210
211    private int getWeightedTemp(ThermalSensor ts, int rawSensorTemp) {
212        int curZoneTemp = 0;
213        Integer weights[], order[];
214        ThermalSensorAttrib sa = mThermalSensorsAttribMap.get(ts.getSensorName());
215
216        // No point in calculating 'WeightedTemp' on an 'anyway invalid' temperature
217        if (rawSensorTemp == ThermalManager.INVALID_TEMP || sa == null) {
218            return ThermalManager.INVALID_TEMP;
219        }
220
221        weights = sa.getWeights();
222        order = sa.getOrder();
223        if (weights == null && order == null) return rawSensorTemp;
224        if (weights != null) {
225            if (order == null) {
226                // only first weight will be considered
227                return (weights[0] * rawSensorTemp) / 1000;
228            } else if (order != null && weights.length == order.length) {
229                // if order array is provided in xml,
230                // it should be of same size as weights array
231                int sensorTemp = 0;
232                for (int i = 0; i < weights.length; i++) {
233                    // Divide by 1000 to convert to mC
234                    sensorTemp += (weights[i] * (int) Math.pow(rawSensorTemp, order[i])) / 1000;
235                }
236                return sensorTemp;
237            }
238        }
239        // for every other mismatch return the raw sensor temp
240        return rawSensorTemp;
241    }
242}
243