1 /* 2 * Copyright (C) 2018 The Android Open Source Project 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.android.server.power; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.hardware.thermal.V1_0.ThermalStatus; 22 import android.hardware.thermal.V1_0.ThermalStatusCode; 23 import android.hardware.thermal.V1_1.IThermalCallback; 24 import android.hardware.thermal.V2_0.IThermalChangedCallback; 25 import android.hardware.thermal.V2_0.ThrottlingSeverity; 26 import android.os.Binder; 27 import android.os.CoolingDevice; 28 import android.os.HwBinder; 29 import android.os.IThermalEventListener; 30 import android.os.IThermalService; 31 import android.os.IThermalStatusListener; 32 import android.os.PowerManager; 33 import android.os.Process; 34 import android.os.RemoteCallbackList; 35 import android.os.RemoteException; 36 import android.os.ResultReceiver; 37 import android.os.ShellCallback; 38 import android.os.ShellCommand; 39 import android.os.Temperature; 40 import android.util.ArrayMap; 41 import android.util.Slog; 42 43 import com.android.internal.annotations.GuardedBy; 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.util.DumpUtils; 46 import com.android.server.FgThread; 47 import com.android.server.SystemService; 48 49 import java.io.FileDescriptor; 50 import java.io.PrintWriter; 51 import java.util.ArrayList; 52 import java.util.Collection; 53 import java.util.Iterator; 54 import java.util.List; 55 import java.util.NoSuchElementException; 56 import java.util.concurrent.atomic.AtomicBoolean; 57 58 /** 59 * This is a system service that listens to HAL thermal events and dispatch those to listeners. 60 * <p>The service will also trigger actions based on severity of the throttling status.</p> 61 * 62 * @hide 63 */ 64 public class ThermalManagerService extends SystemService { 65 private static final String TAG = ThermalManagerService.class.getSimpleName(); 66 67 /** Lock to protect listen list. */ 68 private final Object mLock = new Object(); 69 70 /** 71 * Registered observers of the thermal events. Cookie is used to store type as Integer, null 72 * means no filter. 73 */ 74 @GuardedBy("mLock") 75 private final RemoteCallbackList<IThermalEventListener> mThermalEventListeners = 76 new RemoteCallbackList<>(); 77 78 /** Registered observers of the thermal status. */ 79 @GuardedBy("mLock") 80 private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners = 81 new RemoteCallbackList<>(); 82 83 /** Current thermal status */ 84 @GuardedBy("mLock") 85 private int mStatus; 86 87 /** If override status takes effect*/ 88 @GuardedBy("mLock") 89 private boolean mIsStatusOverride; 90 91 /** Current thermal map, key as name */ 92 @GuardedBy("mLock") 93 private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>(); 94 95 /** HAL wrapper. */ 96 private ThermalHalWrapper mHalWrapper; 97 98 /** Hal ready. */ 99 private final AtomicBoolean mHalReady = new AtomicBoolean(); 100 101 /** Invalid throttling status */ 102 private static final int INVALID_THROTTLING = Integer.MIN_VALUE; 103 104 public ThermalManagerService(Context context) { 105 this(context, null); 106 } 107 108 @VisibleForTesting 109 ThermalManagerService(Context context, @Nullable ThermalHalWrapper halWrapper) { 110 super(context); 111 mHalWrapper = halWrapper; 112 // Initialize to invalid to send status onActivityManagerReady 113 mStatus = INVALID_THROTTLING; 114 } 115 116 @Override 117 public void onStart() { 118 publishBinderService(Context.THERMAL_SERVICE, mService); 119 } 120 121 @Override 122 public void onBootPhase(int phase) { 123 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { 124 onActivityManagerReady(); 125 } 126 } 127 128 private void onActivityManagerReady() { 129 synchronized (mLock) { 130 // Connect to HAL and post to listeners. 131 boolean halConnected = (mHalWrapper != null); 132 if (!halConnected) { 133 mHalWrapper = new ThermalHal20Wrapper(); 134 halConnected = mHalWrapper.connectToHal(); 135 } 136 if (!halConnected) { 137 mHalWrapper = new ThermalHal11Wrapper(); 138 halConnected = mHalWrapper.connectToHal(); 139 } 140 if (!halConnected) { 141 mHalWrapper = new ThermalHal10Wrapper(); 142 halConnected = mHalWrapper.connectToHal(); 143 } 144 mHalWrapper.setCallback(this::onTemperatureChangedCallback); 145 if (!halConnected) { 146 return; 147 } 148 List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(false, 149 0); 150 final int count = temperatures.size(); 151 for (int i = 0; i < count; i++) { 152 onTemperatureChanged(temperatures.get(i), false); 153 } 154 onTemperatureMapChangedLocked(); 155 mHalReady.set(true); 156 } 157 } 158 159 private void postStatusListener(IThermalStatusListener listener) { 160 final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> { 161 try { 162 listener.onStatusChange(mStatus); 163 } catch (RemoteException | RuntimeException e) { 164 Slog.e(TAG, "Thermal callback failed to call", e); 165 } 166 }); 167 if (!thermalCallbackQueued) { 168 Slog.e(TAG, "Thermal callback failed to queue"); 169 } 170 } 171 172 private void notifyStatusListenersLocked() { 173 if (!Temperature.isValidStatus(mStatus)) { 174 return; 175 } 176 final int length = mThermalStatusListeners.beginBroadcast(); 177 try { 178 for (int i = 0; i < length; i++) { 179 final IThermalStatusListener listener = 180 mThermalStatusListeners.getBroadcastItem(i); 181 postStatusListener(listener); 182 } 183 } finally { 184 mThermalStatusListeners.finishBroadcast(); 185 } 186 } 187 188 private void onTemperatureMapChangedLocked() { 189 int newStatus = INVALID_THROTTLING; 190 final int count = mTemperatureMap.size(); 191 for (int i = 0; i < count; i++) { 192 Temperature t = mTemperatureMap.valueAt(i); 193 if (t.getStatus() >= newStatus) { 194 newStatus = t.getStatus(); 195 } 196 } 197 // Do not update if override from shell 198 if (!mIsStatusOverride) { 199 setStatusLocked(newStatus); 200 } 201 } 202 203 private void setStatusLocked(int newStatus) { 204 if (newStatus != mStatus) { 205 mStatus = newStatus; 206 notifyStatusListenersLocked(); 207 } 208 } 209 210 private void postEventListenerCurrentTemperatures(IThermalEventListener listener, 211 @Nullable Integer type) { 212 synchronized (mLock) { 213 final int count = mTemperatureMap.size(); 214 for (int i = 0; i < count; i++) { 215 postEventListener(mTemperatureMap.valueAt(i), listener, 216 type); 217 } 218 } 219 } 220 221 private void postEventListener(Temperature temperature, 222 IThermalEventListener listener, 223 @Nullable Integer type) { 224 // Skip if listener registered with a different type 225 if (type != null && type != temperature.getType()) { 226 return; 227 } 228 final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> { 229 try { 230 listener.notifyThrottling(temperature); 231 } catch (RemoteException | RuntimeException e) { 232 Slog.e(TAG, "Thermal callback failed to call", e); 233 } 234 }); 235 if (!thermalCallbackQueued) { 236 Slog.e(TAG, "Thermal callback failed to queue"); 237 } 238 } 239 240 private void notifyEventListenersLocked(Temperature temperature) { 241 final int length = mThermalEventListeners.beginBroadcast(); 242 try { 243 for (int i = 0; i < length; i++) { 244 final IThermalEventListener listener = 245 mThermalEventListeners.getBroadcastItem(i); 246 final Integer type = 247 (Integer) mThermalEventListeners.getBroadcastCookie(i); 248 postEventListener(temperature, listener, type); 249 } 250 } finally { 251 mThermalEventListeners.finishBroadcast(); 252 } 253 } 254 255 private void shutdownIfNeeded(Temperature temperature) { 256 if (temperature.getStatus() != Temperature.THROTTLING_SHUTDOWN) { 257 return; 258 } 259 final PowerManager powerManager = getContext().getSystemService(PowerManager.class); 260 switch (temperature.getType()) { 261 case Temperature.TYPE_CPU: 262 // Fall through 263 case Temperature.TYPE_GPU: 264 // Fall through 265 case Temperature.TYPE_NPU: 266 // Fall through 267 case Temperature.TYPE_SKIN: 268 powerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false); 269 break; 270 case Temperature.TYPE_BATTERY: 271 powerManager.shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false); 272 break; 273 } 274 } 275 276 private void onTemperatureChanged(Temperature temperature, boolean sendStatus) { 277 shutdownIfNeeded(temperature); 278 synchronized (mLock) { 279 Temperature old = mTemperatureMap.put(temperature.getName(), temperature); 280 if (old != null) { 281 if (old.getStatus() != temperature.getStatus()) { 282 notifyEventListenersLocked(temperature); 283 } 284 } else { 285 notifyEventListenersLocked(temperature); 286 } 287 if (sendStatus) { 288 onTemperatureMapChangedLocked(); 289 } 290 } 291 } 292 293 /* HwBinder callback **/ 294 private void onTemperatureChangedCallback(Temperature temperature) { 295 final long token = Binder.clearCallingIdentity(); 296 try { 297 onTemperatureChanged(temperature, true); 298 } finally { 299 Binder.restoreCallingIdentity(token); 300 } 301 } 302 303 @VisibleForTesting 304 final IThermalService.Stub mService = new IThermalService.Stub() { 305 @Override 306 public boolean registerThermalEventListener(IThermalEventListener listener) { 307 getContext().enforceCallingOrSelfPermission( 308 android.Manifest.permission.DEVICE_POWER, null); 309 synchronized (mLock) { 310 final long token = Binder.clearCallingIdentity(); 311 try { 312 if (!mThermalEventListeners.register(listener, null)) { 313 return false; 314 } 315 // Notify its callback after new client registered. 316 postEventListenerCurrentTemperatures(listener, null); 317 return true; 318 } finally { 319 Binder.restoreCallingIdentity(token); 320 } 321 } 322 } 323 324 @Override 325 public boolean registerThermalEventListenerWithType(IThermalEventListener listener, 326 int type) { 327 getContext().enforceCallingOrSelfPermission( 328 android.Manifest.permission.DEVICE_POWER, null); 329 synchronized (mLock) { 330 final long token = Binder.clearCallingIdentity(); 331 try { 332 if (!mThermalEventListeners.register(listener, new Integer(type))) { 333 return false; 334 } 335 // Notify its callback after new client registered. 336 postEventListenerCurrentTemperatures(listener, new Integer(type)); 337 return true; 338 } finally { 339 Binder.restoreCallingIdentity(token); 340 } 341 } 342 } 343 344 @Override 345 public boolean unregisterThermalEventListener(IThermalEventListener listener) { 346 getContext().enforceCallingOrSelfPermission( 347 android.Manifest.permission.DEVICE_POWER, null); 348 synchronized (mLock) { 349 final long token = Binder.clearCallingIdentity(); 350 try { 351 return mThermalEventListeners.unregister(listener); 352 } finally { 353 Binder.restoreCallingIdentity(token); 354 } 355 } 356 } 357 358 @Override 359 public List<Temperature> getCurrentTemperatures() { 360 getContext().enforceCallingOrSelfPermission( 361 android.Manifest.permission.DEVICE_POWER, null); 362 final long token = Binder.clearCallingIdentity(); 363 try { 364 if (!mHalReady.get()) { 365 return new ArrayList<>(); 366 } 367 return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */); 368 } finally { 369 Binder.restoreCallingIdentity(token); 370 } 371 } 372 373 @Override 374 public List<Temperature> getCurrentTemperaturesWithType(int type) { 375 getContext().enforceCallingOrSelfPermission( 376 android.Manifest.permission.DEVICE_POWER, null); 377 final long token = Binder.clearCallingIdentity(); 378 try { 379 if (!mHalReady.get()) { 380 return new ArrayList<>(); 381 } 382 return mHalWrapper.getCurrentTemperatures(true, type); 383 } finally { 384 Binder.restoreCallingIdentity(token); 385 } 386 } 387 388 @Override 389 public boolean registerThermalStatusListener(IThermalStatusListener listener) { 390 synchronized (mLock) { 391 // Notify its callback after new client registered. 392 final long token = Binder.clearCallingIdentity(); 393 try { 394 if (!mThermalStatusListeners.register(listener)) { 395 return false; 396 } 397 // Notify its callback after new client registered. 398 postStatusListener(listener); 399 return true; 400 } finally { 401 Binder.restoreCallingIdentity(token); 402 } 403 } 404 } 405 406 @Override 407 public boolean unregisterThermalStatusListener(IThermalStatusListener listener) { 408 synchronized (mLock) { 409 final long token = Binder.clearCallingIdentity(); 410 try { 411 return mThermalStatusListeners.unregister(listener); 412 } finally { 413 Binder.restoreCallingIdentity(token); 414 } 415 } 416 } 417 418 @Override 419 public int getCurrentThermalStatus() { 420 synchronized (mLock) { 421 final long token = Binder.clearCallingIdentity(); 422 try { 423 return Temperature.isValidStatus(mStatus) ? mStatus 424 : Temperature.THROTTLING_NONE; 425 } finally { 426 Binder.restoreCallingIdentity(token); 427 } 428 } 429 } 430 431 @Override 432 public List<CoolingDevice> getCurrentCoolingDevices() { 433 getContext().enforceCallingOrSelfPermission( 434 android.Manifest.permission.DEVICE_POWER, null); 435 final long token = Binder.clearCallingIdentity(); 436 try { 437 if (!mHalReady.get()) { 438 return new ArrayList<>(); 439 } 440 return mHalWrapper.getCurrentCoolingDevices(false, 0); 441 } finally { 442 Binder.restoreCallingIdentity(token); 443 } 444 } 445 446 @Override 447 public List<CoolingDevice> getCurrentCoolingDevicesWithType(int type) { 448 getContext().enforceCallingOrSelfPermission( 449 android.Manifest.permission.DEVICE_POWER, null); 450 final long token = Binder.clearCallingIdentity(); 451 try { 452 if (!mHalReady.get()) { 453 return new ArrayList<>(); 454 } 455 return mHalWrapper.getCurrentCoolingDevices(true, type); 456 } finally { 457 Binder.restoreCallingIdentity(token); 458 } 459 } 460 461 private void dumpItemsLocked(PrintWriter pw, String prefix, 462 Collection<?> items) { 463 for (Iterator iterator = items.iterator(); iterator.hasNext();) { 464 pw.println(prefix + iterator.next().toString()); 465 } 466 } 467 468 @Override 469 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 470 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) { 471 return; 472 } 473 final long token = Binder.clearCallingIdentity(); 474 try { 475 synchronized (mLock) { 476 pw.println("IsStatusOverride: " + mIsStatusOverride); 477 pw.println("ThermalEventListeners:"); 478 mThermalEventListeners.dump(pw, "\t"); 479 pw.println("ThermalStatusListeners:"); 480 mThermalStatusListeners.dump(pw, "\t"); 481 pw.println("Thermal Status: " + mStatus); 482 pw.println("Cached temperatures:"); 483 dumpItemsLocked(pw, "\t", mTemperatureMap.values()); 484 pw.println("HAL Ready: " + mHalReady.get()); 485 if (mHalReady.get()) { 486 pw.println("HAL connection:"); 487 mHalWrapper.dump(pw, "\t"); 488 pw.println("Current temperatures from HAL:"); 489 dumpItemsLocked(pw, "\t", 490 mHalWrapper.getCurrentTemperatures(false, 0)); 491 pw.println("Current cooling devices from HAL:"); 492 dumpItemsLocked(pw, "\t", 493 mHalWrapper.getCurrentCoolingDevices(false, 0)); 494 } 495 } 496 } finally { 497 Binder.restoreCallingIdentity(token); 498 } 499 } 500 501 private boolean isCallerShell() { 502 final int callingUid = Binder.getCallingUid(); 503 return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; 504 } 505 506 @Override 507 public void onShellCommand(FileDescriptor in, FileDescriptor out, 508 FileDescriptor err, String[] args, ShellCallback callback, 509 ResultReceiver resultReceiver) { 510 if (!isCallerShell()) { 511 Slog.w(TAG, "Only shell is allowed to call thermalservice shell commands"); 512 return; 513 } 514 (new ThermalShellCommand()).exec( 515 this, in, out, err, args, callback, resultReceiver); 516 } 517 518 }; 519 520 class ThermalShellCommand extends ShellCommand { 521 @Override 522 public int onCommand(String cmd) { 523 switch(cmd != null ? cmd : "") { 524 case "override-status": 525 return runOverrideStatus(); 526 case "reset": 527 return runReset(); 528 default: 529 return handleDefaultCommands(cmd); 530 } 531 } 532 533 private int runReset() { 534 final long token = Binder.clearCallingIdentity(); 535 try { 536 synchronized (mLock) { 537 mIsStatusOverride = false; 538 onTemperatureMapChangedLocked(); 539 return 0; 540 } 541 } finally { 542 Binder.restoreCallingIdentity(token); 543 } 544 } 545 546 private int runOverrideStatus() { 547 final long token = Binder.clearCallingIdentity(); 548 try { 549 final PrintWriter pw = getOutPrintWriter(); 550 int status; 551 try { 552 status = Integer.parseInt(getNextArgRequired()); 553 } catch (RuntimeException ex) { 554 pw.println("Error: " + ex.toString()); 555 return -1; 556 } 557 if (!Temperature.isValidStatus(status)) { 558 pw.println("Invalid status: " + status); 559 return -1; 560 } 561 synchronized (mLock) { 562 mIsStatusOverride = true; 563 setStatusLocked(status); 564 } 565 return 0; 566 } finally { 567 Binder.restoreCallingIdentity(token); 568 } 569 } 570 571 @Override 572 public void onHelp() { 573 final PrintWriter pw = getOutPrintWriter(); 574 pw.println("Thermal service (thermalservice) commands:"); 575 pw.println(" help"); 576 pw.println(" Print this help text."); 577 pw.println(""); 578 pw.println(" override-status STATUS"); 579 pw.println(" sets and locks the thermal status of the device to STATUS."); 580 pw.println(" status code is defined in android.os.Temperature."); 581 pw.println(" reset"); 582 pw.println(" unlocks the thermal status of the device."); 583 pw.println(); 584 } 585 } 586 587 abstract static class ThermalHalWrapper { 588 protected static final String TAG = ThermalHalWrapper.class.getSimpleName(); 589 590 /** Lock to protect HAL handle. */ 591 protected final Object mHalLock = new Object(); 592 593 @FunctionalInterface 594 interface TemperatureChangedCallback { 595 void onValues(Temperature temperature); 596 } 597 598 /** Temperature callback. */ 599 protected TemperatureChangedCallback mCallback; 600 601 /** Cookie for matching the right end point. */ 602 protected static final int THERMAL_HAL_DEATH_COOKIE = 5612; 603 604 @VisibleForTesting 605 protected void setCallback(TemperatureChangedCallback cb) { 606 mCallback = cb; 607 } 608 609 protected abstract List<Temperature> getCurrentTemperatures(boolean shouldFilter, 610 int type); 611 612 protected abstract List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter, 613 int type); 614 615 protected abstract boolean connectToHal(); 616 617 protected abstract void dump(PrintWriter pw, String prefix); 618 619 protected void resendCurrentTemperatures() { 620 synchronized (mHalLock) { 621 List<Temperature> temperatures = getCurrentTemperatures(false, 0); 622 final int count = temperatures.size(); 623 for (int i = 0; i < count; i++) { 624 mCallback.onValues(temperatures.get(i)); 625 } 626 } 627 } 628 629 final class DeathRecipient implements HwBinder.DeathRecipient { 630 @Override 631 public void serviceDied(long cookie) { 632 if (cookie == THERMAL_HAL_DEATH_COOKIE) { 633 Slog.e(TAG, "Thermal HAL service died cookie: " + cookie); 634 synchronized (mHalLock) { 635 connectToHal(); 636 // Post to listeners after reconnect to HAL. 637 resendCurrentTemperatures(); 638 } 639 } 640 } 641 } 642 } 643 644 645 static class ThermalHal10Wrapper extends ThermalHalWrapper { 646 /** Proxy object for the Thermal HAL 1.0 service. */ 647 @GuardedBy("mHalLock") 648 private android.hardware.thermal.V1_0.IThermal mThermalHal10 = null; 649 650 @Override 651 protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, 652 int type) { 653 synchronized (mHalLock) { 654 List<Temperature> ret = new ArrayList<>(); 655 if (mThermalHal10 == null) { 656 return ret; 657 } 658 try { 659 mThermalHal10.getTemperatures( 660 (ThermalStatus status, 661 ArrayList<android.hardware.thermal.V1_0.Temperature> 662 temperatures) -> { 663 if (ThermalStatusCode.SUCCESS == status.code) { 664 for (android.hardware.thermal.V1_0.Temperature 665 temperature : temperatures) { 666 if (shouldFilter && type != temperature.type) { 667 continue; 668 } 669 // Thermal HAL 1.0 doesn't report current throttling status 670 ret.add(new Temperature( 671 temperature.currentValue, temperature.type, 672 temperature.name, 673 Temperature.THROTTLING_NONE)); 674 } 675 } else { 676 Slog.e(TAG, 677 "Couldn't get temperatures because of HAL error: " 678 + status.debugMessage); 679 } 680 681 }); 682 } catch (RemoteException e) { 683 Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e); 684 connectToHal(); 685 } 686 return ret; 687 } 688 } 689 690 @Override 691 protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter, 692 int type) { 693 synchronized (mHalLock) { 694 List<CoolingDevice> ret = new ArrayList<>(); 695 if (mThermalHal10 == null) { 696 return ret; 697 } 698 try { 699 mThermalHal10.getCoolingDevices((status, coolingDevices) -> { 700 if (ThermalStatusCode.SUCCESS == status.code) { 701 for (android.hardware.thermal.V1_0.CoolingDevice 702 coolingDevice : coolingDevices) { 703 if (shouldFilter && type != coolingDevice.type) { 704 continue; 705 } 706 ret.add(new CoolingDevice( 707 (long) coolingDevice.currentValue, 708 coolingDevice.type, 709 coolingDevice.name)); 710 } 711 } else { 712 Slog.e(TAG, 713 "Couldn't get cooling device because of HAL error: " 714 + status.debugMessage); 715 } 716 717 }); 718 } catch (RemoteException e) { 719 Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting...", e); 720 connectToHal(); 721 } 722 return ret; 723 } 724 } 725 726 @Override 727 protected boolean connectToHal() { 728 synchronized (mHalLock) { 729 try { 730 mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService(); 731 mThermalHal10.linkToDeath(new DeathRecipient(), 732 THERMAL_HAL_DEATH_COOKIE); 733 Slog.i(TAG, 734 "Thermal HAL 1.0 service connected, no thermal call back will be " 735 + "called due to legacy API."); 736 } catch (NoSuchElementException | RemoteException e) { 737 Slog.e(TAG, 738 "Thermal HAL 1.0 service not connected."); 739 mThermalHal10 = null; 740 } 741 return (mThermalHal10 != null); 742 } 743 } 744 745 @Override 746 protected void dump(PrintWriter pw, String prefix) { 747 synchronized (mHalLock) { 748 pw.print(prefix); 749 pw.println("ThermalHAL 1.0 connected: " + (mThermalHal10 != null ? "yes" 750 : "no")); 751 } 752 } 753 } 754 755 static class ThermalHal11Wrapper extends ThermalHalWrapper { 756 /** Proxy object for the Thermal HAL 1.1 service. */ 757 @GuardedBy("mHalLock") 758 private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null; 759 760 /** HWbinder callback for Thermal HAL 1.1. */ 761 private final IThermalCallback.Stub mThermalCallback11 = 762 new IThermalCallback.Stub() { 763 @Override 764 public void notifyThrottling(boolean isThrottling, 765 android.hardware.thermal.V1_0.Temperature temperature) { 766 Temperature thermalSvcTemp = new Temperature( 767 temperature.currentValue, temperature.type, temperature.name, 768 isThrottling ? ThrottlingSeverity.SEVERE 769 : ThrottlingSeverity.NONE); 770 final long token = Binder.clearCallingIdentity(); 771 try { 772 mCallback.onValues(thermalSvcTemp); 773 } finally { 774 Binder.restoreCallingIdentity(token); 775 } 776 } 777 }; 778 779 @Override 780 protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, 781 int type) { 782 synchronized (mHalLock) { 783 List<Temperature> ret = new ArrayList<>(); 784 if (mThermalHal11 == null) { 785 return ret; 786 } 787 try { 788 mThermalHal11.getTemperatures( 789 (ThermalStatus status, 790 ArrayList<android.hardware.thermal.V1_0.Temperature> 791 temperatures) -> { 792 if (ThermalStatusCode.SUCCESS == status.code) { 793 for (android.hardware.thermal.V1_0.Temperature 794 temperature : temperatures) { 795 if (shouldFilter && type != temperature.type) { 796 continue; 797 } 798 // Thermal HAL 1.1 doesn't report current throttling status 799 ret.add(new Temperature( 800 temperature.currentValue, temperature.type, 801 temperature.name, 802 Temperature.THROTTLING_NONE)); 803 } 804 } else { 805 Slog.e(TAG, 806 "Couldn't get temperatures because of HAL error: " 807 + status.debugMessage); 808 } 809 810 }); 811 } catch (RemoteException e) { 812 Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e); 813 connectToHal(); 814 } 815 return ret; 816 } 817 } 818 819 @Override 820 protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter, 821 int type) { 822 synchronized (mHalLock) { 823 List<CoolingDevice> ret = new ArrayList<>(); 824 if (mThermalHal11 == null) { 825 return ret; 826 } 827 try { 828 mThermalHal11.getCoolingDevices((status, coolingDevices) -> { 829 if (ThermalStatusCode.SUCCESS == status.code) { 830 for (android.hardware.thermal.V1_0.CoolingDevice 831 coolingDevice : coolingDevices) { 832 if (shouldFilter && type != coolingDevice.type) { 833 continue; 834 } 835 ret.add(new CoolingDevice( 836 (long) coolingDevice.currentValue, 837 coolingDevice.type, 838 coolingDevice.name)); 839 } 840 } else { 841 Slog.e(TAG, 842 "Couldn't get cooling device because of HAL error: " 843 + status.debugMessage); 844 } 845 846 }); 847 } catch (RemoteException e) { 848 Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting...", e); 849 connectToHal(); 850 } 851 return ret; 852 } 853 } 854 855 @Override 856 protected boolean connectToHal() { 857 synchronized (mHalLock) { 858 try { 859 mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService(); 860 mThermalHal11.linkToDeath(new DeathRecipient(), 861 THERMAL_HAL_DEATH_COOKIE); 862 mThermalHal11.registerThermalCallback(mThermalCallback11); 863 } catch (NoSuchElementException | RemoteException e) { 864 Slog.e(TAG, 865 "Thermal HAL 1.1 service not connected, no thermal call back will be " 866 + "called."); 867 mThermalHal11 = null; 868 } 869 return (mThermalHal11 != null); 870 } 871 } 872 873 @Override 874 protected void dump(PrintWriter pw, String prefix) { 875 synchronized (mHalLock) { 876 pw.print(prefix); 877 pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes" 878 : "no")); 879 } 880 } 881 } 882 883 static class ThermalHal20Wrapper extends ThermalHalWrapper { 884 /** Proxy object for the Thermal HAL 2.0 service. */ 885 @GuardedBy("mHalLock") 886 private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null; 887 888 /** HWbinder callback for Thermal HAL 2.0. */ 889 private final IThermalChangedCallback.Stub mThermalCallback20 = 890 new IThermalChangedCallback.Stub() { 891 @Override 892 public void notifyThrottling( 893 android.hardware.thermal.V2_0.Temperature temperature) { 894 Temperature thermalSvcTemp = new Temperature( 895 temperature.value, temperature.type, temperature.name, 896 temperature.throttlingStatus); 897 final long token = Binder.clearCallingIdentity(); 898 try { 899 mCallback.onValues(thermalSvcTemp); 900 } finally { 901 Binder.restoreCallingIdentity(token); 902 } 903 } 904 }; 905 906 @Override 907 protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, 908 int type) { 909 synchronized (mHalLock) { 910 List<Temperature> ret = new ArrayList<>(); 911 if (mThermalHal20 == null) { 912 return ret; 913 } 914 try { 915 mThermalHal20.getCurrentTemperatures(shouldFilter, type, 916 (status, temperatures) -> { 917 if (ThermalStatusCode.SUCCESS == status.code) { 918 for (android.hardware.thermal.V2_0.Temperature 919 temperature : temperatures) { 920 ret.add(new Temperature( 921 temperature.value, temperature.type, 922 temperature.name, 923 temperature.throttlingStatus)); 924 } 925 } else { 926 Slog.e(TAG, 927 "Couldn't get temperatures because of HAL error: " 928 + status.debugMessage); 929 } 930 931 }); 932 } catch (RemoteException e) { 933 Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e); 934 connectToHal(); 935 } 936 return ret; 937 } 938 } 939 940 @Override 941 protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter, 942 int type) { 943 synchronized (mHalLock) { 944 List<CoolingDevice> ret = new ArrayList<>(); 945 if (mThermalHal20 == null) { 946 return ret; 947 } 948 try { 949 mThermalHal20.getCurrentCoolingDevices(shouldFilter, type, 950 (status, coolingDevices) -> { 951 if (ThermalStatusCode.SUCCESS == status.code) { 952 for (android.hardware.thermal.V2_0.CoolingDevice 953 coolingDevice : coolingDevices) { 954 ret.add(new CoolingDevice( 955 coolingDevice.value, coolingDevice.type, 956 coolingDevice.name)); 957 } 958 } else { 959 Slog.e(TAG, 960 "Couldn't get cooling device because of HAL error: " 961 + status.debugMessage); 962 } 963 964 }); 965 } catch (RemoteException e) { 966 Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting...", e); 967 connectToHal(); 968 } 969 return ret; 970 } 971 } 972 973 @Override 974 protected boolean connectToHal() { 975 synchronized (mHalLock) { 976 try { 977 mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService(); 978 mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE); 979 mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false, 980 0 /* not used */); 981 } catch (NoSuchElementException | RemoteException e) { 982 Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1."); 983 mThermalHal20 = null; 984 } 985 return (mThermalHal20 != null); 986 } 987 } 988 989 @Override 990 protected void dump(PrintWriter pw, String prefix) { 991 synchronized (mHalLock) { 992 pw.print(prefix); 993 pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes" 994 : "no")); 995 } 996 } 997 } 998 999 } 1000