Merge "docs: fix a wrong link" into mnc-ub-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index c5ca20b..1e7b94e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -32714,6 +32714,7 @@
     field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
     field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
+    field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
     field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 9c0d931..371c923 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -239,6 +239,8 @@
         }
 
         mTextureView.setSurfaceTextureListener(null);
+
+        mThread.quit();
     }
 
     private void attachToSurfaceWhenReady() {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 12c2632..6bbee56 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3022,6 +3022,39 @@
     public static final String EXTRA_PROCESS_TEXT_READONLY =
             "android.intent.extra.PROCESS_TEXT_READONLY";
 
+    /**
+     * Broadcast action: reports when a new thermal event has been reached. When the device
+     * is reaching its maximum temperatue, the thermal level reported
+     * {@hide}
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_THERMAL_EVENT = "android.intent.action.THERMAL_EVENT";
+
+    /** {@hide} */
+    public static final String EXTRA_THERMAL_STATE = "android.intent.extra.THERMAL_STATE";
+
+    /**
+     * Thermal state when the device is normal. This state is sent in the
+     * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+     * {@hide}
+     */
+    public static final int EXTRA_THERMAL_STATE_NORMAL = 0;
+
+    /**
+     * Thermal state where the device is approaching its maximum threshold. This state is sent in
+     * the {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+     * {@hide}
+     */
+    public static final int EXTRA_THERMAL_STATE_WARNING = 1;
+
+    /**
+     * Thermal state where the device has reached its maximum threshold. This state is sent in the
+     * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+     * {@hide}
+     */
+    public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2;
+
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent categories (see addCategory()).
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e39bf60..8cf2dabdb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 131 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 132 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b051de4..0f46983 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -65,6 +65,7 @@
     <protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
     <protected-broadcast android:name="android.intent.action.REBOOT" />
     <protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
+    <protected-broadcast android:name="android.intent.action.THERMAL_EVENT" />
     <protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
     <protected-broadcast android:name="android.intent.action.USER_ADDED" />
     <protected-broadcast android:name="android.intent.action.USER_REMOVED" />
diff --git a/packages/SystemUI/res/values-en/donottranslate.xml b/packages/SystemUI/res/values-en/donottranslate.xml
new file mode 100644
index 0000000..9f04e1f
--- /dev/null
+++ b/packages/SystemUI/res/values-en/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ 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
+  -->
+
+<resources>
+    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+    <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time_fast</item>
+    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+    <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time_slowly</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index 351a1fd..30ff704 100644
--- a/packages/SystemUI/res/values/donottranslate.xml
+++ b/packages/SystemUI/res/values/donottranslate.xml
@@ -20,4 +20,10 @@
     <!-- Date format for display: should match the lockscreen in /policy.  -->
     <string name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</string>
 
+    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+    <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time</item>
+
+    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+    <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time</item>
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3eac84f..dfa85ce 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -793,6 +793,12 @@
     <!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]-->
     <string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
 
+    <!-- Indication on the keyguard that is shown when the device is charging rapidly. Should match keyguard_plugged_in_charging_fast [CHAR LIMIT=40]-->
+    <string name="keyguard_indication_charging_time_fast">Charging rapidly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
+    <!-- Indication on the keyguard that is shown when the device is charging slowly. Should match keyguard_plugged_in_charging_slowly [CHAR LIMIT=40]-->
+    <string name="keyguard_indication_charging_time_slowly">Charging slowly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
     <!-- Related to user switcher --><skip/>
 
     <!-- Accessibility label for the button that opens the user switcher. -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 54f91da..50d274d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -187,32 +187,41 @@
         }
 
         // Try fetching charging time from battery stats.
+        long chargingTimeRemaining = 0;
         try {
-            long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
-            if (chargingTimeRemaining > 0) {
-                String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
-                        mContext, chargingTimeRemaining);
-                return mContext.getResources().getString(
-                        R.string.keyguard_indication_charging_time, chargingTimeFormatted);
-            }
+            chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
+
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IBatteryStats: ", e);
         }
+        final boolean hasChargingTime = chargingTimeRemaining > 0;
 
-        // Fall back to simple charging label.
         int chargingId;
         switch (mChargingSpeed) {
             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
-                chargingId = R.string.keyguard_plugged_in_charging_fast;
+                chargingId = hasChargingTime
+                        ? R.string.keyguard_indication_charging_time_fast_if_translated
+                        : R.string.keyguard_plugged_in_charging_fast;
                 break;
             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
-                chargingId = R.string.keyguard_plugged_in_charging_slowly;
+                chargingId = hasChargingTime
+                        ? R.string.keyguard_indication_charging_time_slowly_if_translated
+                        : R.string.keyguard_plugged_in_charging_slowly;
                 break;
             default:
-                chargingId = R.string.keyguard_plugged_in;
+                chargingId = hasChargingTime
+                        ? R.string.keyguard_indication_charging_time
+                        : R.string.keyguard_plugged_in;
                 break;
         }
-        return mContext.getResources().getString(chargingId);
+
+        if (hasChargingTime) {
+            String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+                    mContext, chargingTimeRemaining);
+            return mContext.getResources().getString(chargingId, chargingTimeFormatted);
+        } else {
+            return mContext.getResources().getString(chargingId);
+        }
     }
 
     KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 46fd28a..5239ede 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -31,6 +31,8 @@
 import android.database.ContentObserver;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
 import android.hardware.display.DisplayManager;
@@ -111,7 +113,7 @@
     private INetworkPolicyManager mNetworkPolicyManager;
     private DisplayManager mDisplayManager;
     private SensorManager mSensorManager;
-    private Sensor mSigMotionSensor;
+    private Sensor mMotionSensor;
     private LocationManager mLocationManager;
     private LocationRequest mLocationRequest;
     private PendingIntent mSensingAlarmIntent;
@@ -123,7 +125,6 @@
     private boolean mForceIdle;
     private boolean mScreenOn;
     private boolean mCharging;
-    private boolean mSigMotionActive;
     private boolean mSensing;
     private boolean mNotMoving;
     private boolean mLocating;
@@ -268,13 +269,57 @@
         }
     };
 
-    private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
-        @Override public void onTrigger(TriggerEvent event) {
+    private final class MotionListener extends TriggerEventListener
+            implements SensorEventListener {
+
+        boolean active = false;
+
+        @Override
+        public void onTrigger(TriggerEvent event) {
             synchronized (DeviceIdleController.this) {
-                significantMotionLocked();
+                active = false;
+                motionLocked();
             }
         }
-    };
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            synchronized (DeviceIdleController.this) {
+                mSensorManager.unregisterListener(this, mMotionSensor);
+                active = false;
+                motionLocked();
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+        public boolean registerLocked() {
+            boolean success = false;
+            if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+                success = mSensorManager.requestTriggerSensor(mMotionListener, mMotionSensor);
+            } else {
+                success = mSensorManager.registerListener(
+                        mMotionListener, mMotionSensor, SensorManager.SENSOR_DELAY_NORMAL);
+            }
+            if (success) {
+                active = true;
+            } else {
+                Slog.e(TAG, "Unable to register for " + mMotionSensor);
+            }
+            return success;
+        }
+
+        public void unregisterLocked() {
+            if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+                mSensorManager.cancelTriggerSensor(mMotionListener, mMotionSensor);
+            } else {
+                mSensorManager.unregisterListener(mMotionListener);
+            }
+            active = false;
+        }
+    }
+    private final MotionListener mMotionListener = new MotionListener();
 
     private final LocationListener mGenericLocationListener = new LocationListener() {
         @Override
@@ -349,7 +394,7 @@
          * This is the time, after becoming inactive, at which we start looking at the
          * motion sensor to determine if the device is being left alone.  We don't do this
          * immediately after going inactive just because we don't want to be continually running
-         * the significant motion sensor whenever the screen is off.
+         * the motion sensor whenever the screen is off.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_INACTIVE_TIMEOUT
          */
@@ -392,7 +437,7 @@
 
         /**
          * This is the time, after the inactive timeout elapses, that we will wait looking
-         * for significant motion until we truly consider the device to be idle.
+         * for motion until we truly consider the device to be idle.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
          */
@@ -886,18 +931,19 @@
                 int sigMotionSensorId = getContext().getResources().getInteger(
                         com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
                 if (sigMotionSensorId > 0) {
-                    mSigMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
+                    mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
                 }
-                if (mSigMotionSensor == null && getContext().getResources().getBoolean(
+                if (mMotionSensor == null && getContext().getResources().getBoolean(
                         com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
-                    mSigMotionSensor = mSensorManager.getDefaultSensor(
-                            Sensor.TYPE_WRIST_TILT_GESTURE);
+                    mMotionSensor = mSensorManager.getDefaultSensor(
+                            Sensor.TYPE_WRIST_TILT_GESTURE, true);
                 }
-                if (mSigMotionSensor == null) {
+                if (mMotionSensor == null) {
                     // As a last ditch, fall back to SMD.
-                    mSigMotionSensor = mSensorManager.getDefaultSensor(
-                            Sensor.TYPE_SIGNIFICANT_MOTION);
+                    mMotionSensor = mSensorManager.getDefaultSensor(
+                            Sensor.TYPE_SIGNIFICANT_MOTION, true);
                 }
+
                 if (getContext().getResources().getBoolean(
                         com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
                     mLocationManager = (LocationManager) getContext().getSystemService(
@@ -1242,7 +1288,7 @@
         cancelAlarmLocked();
         cancelSensingAlarmLocked();
         cancelLocatingLocked();
-        stopMonitoringSignificantMotion();
+        stopMonitoringMotionLocked();
         mAnyMotionDetector.stop();
     }
 
@@ -1271,8 +1317,8 @@
         switch (mState) {
             case STATE_INACTIVE:
                 // We have now been inactive long enough, it is time to start looking
-                // for significant motion and sleep some more while doing so.
-                startMonitoringSignificantMotion();
+                // for motion and sleep some more while doing so.
+                startMonitoringMotionLocked();
                 scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
                 // Reset the upcoming idle delays.
                 mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
@@ -1353,17 +1399,16 @@
         }
     }
 
-    void significantMotionLocked() {
-        if (DEBUG) Slog.d(TAG, "significantMotionLocked()");
-        // When the sensor goes off, its trigger is automatically removed.
-        mSigMotionActive = false;
+    void motionLocked() {
+        if (DEBUG) Slog.d(TAG, "motionLocked()");
+        // The motion sensor will have been disabled at this point
         handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
     }
 
     void handleMotionDetectedLocked(long timeout, String type) {
         // The device is not yet active, so we want to go back to the pending idle
-        // state to wait again for no motion.  Note that we only monitor for significant
-        // motion after moving out of the inactive state, so no need to worry about that.
+        // state to wait again for no motion.  Note that we only monitor for motion
+        // after moving out of the inactive state, so no need to worry about that.
         if (mState != STATE_ACTIVE) {
             scheduleReportActiveLocked(type, Process.myUid());
             mState = STATE_ACTIVE;
@@ -1405,19 +1450,17 @@
         }
     }
 
-    void startMonitoringSignificantMotion() {
-        if (DEBUG) Slog.d(TAG, "startMonitoringSignificantMotion()");
-        if (mSigMotionSensor != null && !mSigMotionActive) {
-            mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
-            mSigMotionActive = true;
+    void startMonitoringMotionLocked() {
+        if (DEBUG) Slog.d(TAG, "startMonitoringMotionLocked()");
+        if (mMotionSensor != null && !mMotionListener.active) {
+            mMotionListener.registerLocked();
         }
     }
 
-    void stopMonitoringSignificantMotion() {
-        if (DEBUG) Slog.d(TAG, "stopMonitoringSignificantMotion()");
-        if (mSigMotionActive) {
-            mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
-            mSigMotionActive = false;
+    void stopMonitoringMotionLocked() {
+        if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
+        if (mMotionSensor != null && mMotionListener.active) {
+            mMotionListener.unregisterLocked();
         }
     }
 
@@ -1446,7 +1489,7 @@
 
     void scheduleAlarmLocked(long delay, boolean idleUntil) {
         if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
-        if (mSigMotionSensor == null) {
+        if (mMotionSensor == null) {
             // If there is no motion sensor on this device, then we won't schedule
             // alarms, because we can't determine if the device is not moving.  This effectively
             // turns off normal execution of device idling, although it is still possible to
@@ -1929,11 +1972,11 @@
 
             pw.print("  mEnabled="); pw.println(mEnabled);
             pw.print("  mForceIdle="); pw.println(mForceIdle);
-            pw.print("  mSigMotionSensor="); pw.println(mSigMotionSensor);
+            pw.print("  mMotionSensor="); pw.println(mMotionSensor);
             pw.print("  mCurDisplay="); pw.println(mCurDisplay);
             pw.print("  mScreenOn="); pw.println(mScreenOn);
             pw.print("  mCharging="); pw.println(mCharging);
-            pw.print("  mSigMotionActive="); pw.println(mSigMotionActive);
+            pw.print("  mMotionActive="); pw.println(mMotionListener.active);
             pw.print("  mSensing="); pw.print(mSensing); pw.print(" mNotMoving=");
                     pw.println(mNotMoving);
             pw.print("  mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
diff --git a/services/core/java/com/android/server/ThermalObserver.java b/services/core/java/com/android/server/ThermalObserver.java
new file mode 100644
index 0000000..aee28fb
--- /dev/null
+++ b/services/core/java/com/android/server/ThermalObserver.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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.android.server;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UEventObserver;
+import android.os.UserHandle;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * ThermalObserver for monitoring temperature changes.
+ */
+public class ThermalObserver extends SystemService {
+    private static final String TAG = "ThermalObserver";
+
+    private static final String CALLSTATE_UEVENT_MATCH =
+            "DEVPATH=/devices/virtual/switch/thermalstate";
+
+    private static final int MSG_THERMAL_STATE_CHANGED = 0;
+
+    private static final int SWITCH_STATE_NORMAL = 0;
+    private static final int SWITCH_STATE_WARNING = 1;
+    private static final int SWITCH_STATE_EXCEEDED = 2;
+
+    private final PowerManager mPowerManager;
+    private final PowerManager.WakeLock mWakeLock;
+
+    private final Object mLock = new Object();
+    private Integer mLastState;
+
+    private final UEventObserver mThermalWarningObserver = new UEventObserver() {
+        @Override
+        public void onUEvent(UEventObserver.UEvent event) {
+            updateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+        }
+    };
+
+    private final Handler mHandler = new Handler(true /*async*/) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_THERMAL_STATE_CHANGED:
+                    handleThermalStateChange(msg.arg1);
+                    mWakeLock.release();
+                    break;
+            }
+        }
+    };
+
+    public ThermalObserver(Context context) {
+        super(context);
+        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+        mThermalWarningObserver.startObserving(CALLSTATE_UEVENT_MATCH);
+    }
+
+    private void updateLocked(int state) {
+        Message message = new Message();
+        message.what = MSG_THERMAL_STATE_CHANGED;
+        message.arg1 = state;
+
+        mWakeLock.acquire();
+        mHandler.sendMessage(message);
+    }
+
+    private void handleThermalStateChange(int state) {
+        synchronized (mLock) {
+            mLastState = state;
+            Intent intent = new Intent(Intent.ACTION_THERMAL_EVENT);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+            final int thermalState;
+
+            switch (state) {
+                case SWITCH_STATE_WARNING:
+                    thermalState = Intent.EXTRA_THERMAL_STATE_WARNING;
+                    break;
+                case SWITCH_STATE_EXCEEDED:
+                    thermalState = Intent.EXTRA_THERMAL_STATE_EXCEEDED;
+                    break;
+                case SWITCH_STATE_NORMAL:
+                default:
+                    thermalState = Intent.EXTRA_THERMAL_STATE_NORMAL;
+                    break;
+            }
+
+            intent.putExtra(Intent.EXTRA_THERMAL_STATE, thermalState);
+
+            getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(TAG, new BinderService());
+    }
+
+    private final class BinderService extends Binder {
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission Denial: can't dump thermal observer service from from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    if (args == null || args.length == 0 || "-a".equals(args[0])) {
+                        pw.println("Current Thermal Observer Service state:");
+                        pw.println("  last state change: "
+                                + (mLastState != null ? mLastState : "none"));
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3effe63..08816b9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -825,6 +825,11 @@
 
             if (!disableNonCoreServices) {
                 mSystemServiceManager.startService(DockObserver.class);
+
+                if (context.getPackageManager().hasSystemFeature
+                        (PackageManager.FEATURE_WATCH)) {
+                    mSystemServiceManager.startService(ThermalObserver.class);
+                }
             }
 
             try {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8779462..b07b018f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -116,6 +116,15 @@
             "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
 
     /**
+     * The {@link android.content.Intent} action used indicate that a phone account was
+     * just unregistered.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_PHONE_ACCOUNT_UNREGISTERED =
+            "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
+
+    /**
      * Activity action: Shows a dialog asking the user whether or not they want to replace the
      * current default Dialer with the one specified in
      * {@link #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME}.