1 /* 2 * Copyright (C) 2021 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.hint; 18 19 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; 20 21 import android.annotation.NonNull; 22 import android.app.ActivityManager; 23 import android.app.ActivityManagerInternal; 24 import android.app.StatsManager; 25 import android.app.UidObserver; 26 import android.content.Context; 27 import android.os.Binder; 28 import android.os.IBinder; 29 import android.os.IHintManager; 30 import android.os.IHintSession; 31 import android.os.PerformanceHintManager; 32 import android.os.Process; 33 import android.os.RemoteException; 34 import android.os.SystemProperties; 35 import android.util.ArrayMap; 36 import android.util.ArraySet; 37 import android.util.SparseArray; 38 import android.util.StatsEvent; 39 40 import com.android.internal.annotations.GuardedBy; 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.util.DumpUtils; 43 import com.android.internal.util.FrameworkStatsLog; 44 import com.android.internal.util.Preconditions; 45 import com.android.server.FgThread; 46 import com.android.server.LocalServices; 47 import com.android.server.SystemService; 48 import com.android.server.utils.Slogf; 49 50 import java.io.FileDescriptor; 51 import java.io.PrintWriter; 52 import java.util.Arrays; 53 import java.util.List; 54 import java.util.Objects; 55 56 /** An hint service implementation that runs in System Server process. */ 57 public final class HintManagerService extends SystemService { 58 private static final String TAG = "HintManagerService"; 59 private static final boolean DEBUG = false; 60 @VisibleForTesting final long mHintSessionPreferredRate; 61 62 // Multi-level map storing all active AppHintSessions. 63 // First level is keyed by the UID of the client process creating the session. 64 // Second level is keyed by an IBinder passed from client process. This is used to observe 65 // when the process exits. The client generally uses the same IBinder object across multiple 66 // sessions, so the value is a set of AppHintSessions. 67 @GuardedBy("mLock") 68 private final ArrayMap<Integer, ArrayMap<IBinder, ArraySet<AppHintSession>>> mActiveSessions; 69 70 /** Lock to protect HAL handles and listen list. */ 71 private final Object mLock = new Object(); 72 73 @VisibleForTesting final MyUidObserver mUidObserver; 74 75 private final NativeWrapper mNativeWrapper; 76 77 private final ActivityManagerInternal mAmInternal; 78 79 private final Context mContext; 80 81 private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint"; 82 private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager"; 83 84 @VisibleForTesting final IHintManager.Stub mService = new BinderService(); 85 HintManagerService(Context context)86 public HintManagerService(Context context) { 87 this(context, new Injector()); 88 } 89 90 @VisibleForTesting HintManagerService(Context context, Injector injector)91 HintManagerService(Context context, Injector injector) { 92 super(context); 93 mContext = context; 94 mActiveSessions = new ArrayMap<>(); 95 mNativeWrapper = injector.createNativeWrapper(); 96 mNativeWrapper.halInit(); 97 mHintSessionPreferredRate = mNativeWrapper.halGetHintSessionPreferredRate(); 98 mUidObserver = new MyUidObserver(); 99 mAmInternal = Objects.requireNonNull( 100 LocalServices.getService(ActivityManagerInternal.class)); 101 } 102 103 @VisibleForTesting 104 static class Injector { createNativeWrapper()105 NativeWrapper createNativeWrapper() { 106 return new NativeWrapper(); 107 } 108 } 109 isHalSupported()110 private boolean isHalSupported() { 111 return mHintSessionPreferredRate != -1; 112 } 113 114 @Override onStart()115 public void onStart() { 116 publishBinderService(Context.PERFORMANCE_HINT_SERVICE, mService); 117 } 118 119 @Override onBootPhase(int phase)120 public void onBootPhase(int phase) { 121 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 122 systemReady(); 123 } 124 if (phase == SystemService.PHASE_BOOT_COMPLETED) { 125 registerStatsCallbacks(); 126 } 127 } 128 systemReady()129 private void systemReady() { 130 Slogf.v(TAG, "Initializing HintManager service..."); 131 try { 132 ActivityManager.getService().registerUidObserver(mUidObserver, 133 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, 134 ActivityManager.PROCESS_STATE_UNKNOWN, null); 135 } catch (RemoteException e) { 136 // ignored; both services live in system_server 137 } 138 139 } 140 registerStatsCallbacks()141 private void registerStatsCallbacks() { 142 final StatsManager statsManager = mContext.getSystemService(StatsManager.class); 143 statsManager.setPullAtomCallback( 144 FrameworkStatsLog.ADPF_SYSTEM_COMPONENT_INFO, 145 null, // use default PullAtomMetadata values 146 DIRECT_EXECUTOR, 147 this::onPullAtom); 148 } 149 onPullAtom(int atomTag, @NonNull List<StatsEvent> data)150 private int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) { 151 if (atomTag == FrameworkStatsLog.ADPF_SYSTEM_COMPONENT_INFO) { 152 final boolean isSurfaceFlingerUsingCpuHint = 153 SystemProperties.getBoolean(PROPERTY_SF_ENABLE_CPU_HINT, false); 154 final boolean isHwuiHintManagerEnabled = 155 SystemProperties.getBoolean(PROPERTY_HWUI_ENABLE_HINT_MANAGER, false); 156 157 data.add(FrameworkStatsLog.buildStatsEvent( 158 FrameworkStatsLog.ADPF_SYSTEM_COMPONENT_INFO, 159 isSurfaceFlingerUsingCpuHint, 160 isHwuiHintManagerEnabled)); 161 } 162 return android.app.StatsManager.PULL_SUCCESS; 163 } 164 165 /** 166 * Wrapper around the static-native methods from native. 167 * 168 * This class exists to allow us to mock static native methods in our tests. If mocking static 169 * methods becomes easier than this in the future, we can delete this class. 170 */ 171 @VisibleForTesting 172 public static class NativeWrapper { nativeInit()173 private native void nativeInit(); 174 nativeCreateHintSession(int tgid, int uid, int[] tids, long durationNanos)175 private static native long nativeCreateHintSession(int tgid, int uid, int[] tids, 176 long durationNanos); 177 nativePauseHintSession(long halPtr)178 private static native void nativePauseHintSession(long halPtr); 179 nativeResumeHintSession(long halPtr)180 private static native void nativeResumeHintSession(long halPtr); 181 nativeCloseHintSession(long halPtr)182 private static native void nativeCloseHintSession(long halPtr); 183 nativeUpdateTargetWorkDuration( long halPtr, long targetDurationNanos)184 private static native void nativeUpdateTargetWorkDuration( 185 long halPtr, long targetDurationNanos); 186 nativeReportActualWorkDuration( long halPtr, long[] actualDurationNanos, long[] timeStampNanos)187 private static native void nativeReportActualWorkDuration( 188 long halPtr, long[] actualDurationNanos, long[] timeStampNanos); 189 nativeSendHint(long halPtr, int hint)190 private static native void nativeSendHint(long halPtr, int hint); 191 nativeSetThreads(long halPtr, int[] tids)192 private static native void nativeSetThreads(long halPtr, int[] tids); 193 nativeGetHintSessionPreferredRate()194 private static native long nativeGetHintSessionPreferredRate(); 195 196 /** Wrapper for HintManager.nativeInit */ halInit()197 public void halInit() { 198 nativeInit(); 199 } 200 201 /** Wrapper for HintManager.nativeCreateHintSession */ halCreateHintSession(int tgid, int uid, int[] tids, long durationNanos)202 public long halCreateHintSession(int tgid, int uid, int[] tids, long durationNanos) { 203 return nativeCreateHintSession(tgid, uid, tids, durationNanos); 204 } 205 206 /** Wrapper for HintManager.nativePauseHintSession */ halPauseHintSession(long halPtr)207 public void halPauseHintSession(long halPtr) { 208 nativePauseHintSession(halPtr); 209 } 210 211 /** Wrapper for HintManager.nativeResumeHintSession */ halResumeHintSession(long halPtr)212 public void halResumeHintSession(long halPtr) { 213 nativeResumeHintSession(halPtr); 214 } 215 216 /** Wrapper for HintManager.nativeCloseHintSession */ halCloseHintSession(long halPtr)217 public void halCloseHintSession(long halPtr) { 218 nativeCloseHintSession(halPtr); 219 } 220 221 /** Wrapper for HintManager.nativeUpdateTargetWorkDuration */ halUpdateTargetWorkDuration(long halPtr, long targetDurationNanos)222 public void halUpdateTargetWorkDuration(long halPtr, long targetDurationNanos) { 223 nativeUpdateTargetWorkDuration(halPtr, targetDurationNanos); 224 } 225 226 /** Wrapper for HintManager.nativeReportActualWorkDuration */ halReportActualWorkDuration( long halPtr, long[] actualDurationNanos, long[] timeStampNanos)227 public void halReportActualWorkDuration( 228 long halPtr, long[] actualDurationNanos, long[] timeStampNanos) { 229 nativeReportActualWorkDuration(halPtr, actualDurationNanos, 230 timeStampNanos); 231 } 232 233 /** Wrapper for HintManager.sendHint */ halSendHint(long halPtr, int hint)234 public void halSendHint(long halPtr, int hint) { 235 nativeSendHint(halPtr, hint); 236 } 237 238 /** Wrapper for HintManager.nativeGetHintSessionPreferredRate */ halGetHintSessionPreferredRate()239 public long halGetHintSessionPreferredRate() { 240 return nativeGetHintSessionPreferredRate(); 241 } 242 243 /** Wrapper for HintManager.nativeSetThreads */ halSetThreads(long halPtr, int[] tids)244 public void halSetThreads(long halPtr, int[] tids) { 245 nativeSetThreads(halPtr, tids); 246 } 247 } 248 249 @VisibleForTesting 250 final class MyUidObserver extends UidObserver { 251 private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); 252 isUidForeground(int uid)253 public boolean isUidForeground(int uid) { 254 synchronized (mLock) { 255 return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) 256 <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; 257 } 258 } 259 260 @Override onUidGone(int uid, boolean disabled)261 public void onUidGone(int uid, boolean disabled) { 262 FgThread.getHandler().post(() -> { 263 synchronized (mLock) { 264 ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(uid); 265 if (tokenMap == null) { 266 return; 267 } 268 for (int i = tokenMap.size() - 1; i >= 0; i--) { 269 // Will remove the session from tokenMap 270 ArraySet<AppHintSession> sessionSet = tokenMap.valueAt(i); 271 for (int j = sessionSet.size() - 1; j >= 0; j--) { 272 sessionSet.valueAt(j).close(); 273 } 274 } 275 mProcStatesCache.delete(uid); 276 } 277 }); 278 } 279 280 /** 281 * The IUidObserver callback is called from the system_server, so it'll be a direct function 282 * call from ActivityManagerService. Do not do heavy logic here. 283 */ 284 @Override onUidStateChanged(int uid, int procState, long procStateSeq, int capability)285 public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { 286 FgThread.getHandler().post(() -> { 287 synchronized (mLock) { 288 mProcStatesCache.put(uid, procState); 289 ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(uid); 290 if (tokenMap == null) { 291 return; 292 } 293 for (ArraySet<AppHintSession> sessionSet : tokenMap.values()) { 294 for (AppHintSession s : sessionSet) { 295 s.onProcStateChanged(); 296 } 297 } 298 } 299 }); 300 } 301 } 302 303 @VisibleForTesting getBinderServiceInstance()304 IHintManager.Stub getBinderServiceInstance() { 305 return mService; 306 } 307 checkTidValid(int uid, int tgid, int [] tids)308 private boolean checkTidValid(int uid, int tgid, int [] tids) { 309 // Make sure all tids belongs to the same UID (including isolated UID), 310 // tids can belong to different application processes. 311 List<Integer> isolatedPids = null; 312 for (int threadId : tids) { 313 final String[] procStatusKeys = new String[] { 314 "Uid:", 315 "Tgid:" 316 }; 317 long[] output = new long[procStatusKeys.length]; 318 Process.readProcLines("/proc/" + threadId + "/status", procStatusKeys, output); 319 int uidOfThreadId = (int) output[0]; 320 int pidOfThreadId = (int) output[1]; 321 322 // use PID check for isolated processes, use UID check for non-isolated processes. 323 if (pidOfThreadId == tgid || uidOfThreadId == uid) { 324 continue; 325 } 326 // Only call into AM if the tid is either isolated or invalid 327 if (isolatedPids == null) { 328 // To avoid deadlock, do not call into AMS if the call is from system. 329 if (uid == Process.SYSTEM_UID) { 330 return false; 331 } 332 isolatedPids = mAmInternal.getIsolatedProcesses(uid); 333 if (isolatedPids == null) { 334 return false; 335 } 336 } 337 if (isolatedPids.contains(pidOfThreadId)) { 338 continue; 339 } 340 return false; 341 } 342 return true; 343 } 344 345 @VisibleForTesting 346 final class BinderService extends IHintManager.Stub { 347 @Override createHintSession(IBinder token, int[] tids, long durationNanos)348 public IHintSession createHintSession(IBinder token, int[] tids, long durationNanos) { 349 if (!isHalSupported()) return null; 350 351 java.util.Objects.requireNonNull(token); 352 java.util.Objects.requireNonNull(tids); 353 Preconditions.checkArgument(tids.length != 0, "tids should" 354 + " not be empty."); 355 356 final int callingUid = Binder.getCallingUid(); 357 final int callingTgid = Process.getThreadGroupLeader(Binder.getCallingPid()); 358 final long identity = Binder.clearCallingIdentity(); 359 try { 360 if (!checkTidValid(callingUid, callingTgid, tids)) { 361 throw new SecurityException("Some tid doesn't belong to the application"); 362 } 363 364 long halSessionPtr = mNativeWrapper.halCreateHintSession(callingTgid, callingUid, 365 tids, durationNanos); 366 if (halSessionPtr == 0) { 367 return null; 368 } 369 370 AppHintSession hs = new AppHintSession(callingUid, callingTgid, tids, token, 371 halSessionPtr, durationNanos); 372 logPerformanceHintSessionAtom(callingUid, halSessionPtr, durationNanos, tids); 373 synchronized (mLock) { 374 ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = 375 mActiveSessions.get(callingUid); 376 if (tokenMap == null) { 377 tokenMap = new ArrayMap<>(1); 378 mActiveSessions.put(callingUid, tokenMap); 379 } 380 ArraySet<AppHintSession> sessionSet = tokenMap.get(token); 381 if (sessionSet == null) { 382 sessionSet = new ArraySet<>(1); 383 tokenMap.put(token, sessionSet); 384 } 385 sessionSet.add(hs); 386 return hs; 387 } 388 } finally { 389 Binder.restoreCallingIdentity(identity); 390 } 391 } 392 393 @Override getHintSessionPreferredRate()394 public long getHintSessionPreferredRate() { 395 return mHintSessionPreferredRate; 396 } 397 398 @Override setHintSessionThreads(@onNull IHintSession hintSession, @NonNull int[] tids)399 public void setHintSessionThreads(@NonNull IHintSession hintSession, @NonNull int[] tids) { 400 AppHintSession appHintSession = (AppHintSession) hintSession; 401 appHintSession.setThreads(tids); 402 } 403 404 @Override getHintSessionThreadIds(@onNull IHintSession hintSession)405 public int[] getHintSessionThreadIds(@NonNull IHintSession hintSession) { 406 AppHintSession appHintSession = (AppHintSession) hintSession; 407 return appHintSession.getThreadIds(); 408 } 409 410 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)411 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 412 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) { 413 return; 414 } 415 synchronized (mLock) { 416 pw.println("HintSessionPreferredRate: " + mHintSessionPreferredRate); 417 pw.println("HAL Support: " + isHalSupported()); 418 pw.println("Active Sessions:"); 419 for (int i = 0; i < mActiveSessions.size(); i++) { 420 pw.println("Uid " + mActiveSessions.keyAt(i).toString() + ":"); 421 ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = 422 mActiveSessions.valueAt(i); 423 for (int j = 0; j < tokenMap.size(); j++) { 424 ArraySet<AppHintSession> sessionSet = tokenMap.valueAt(j); 425 for (int k = 0; k < sessionSet.size(); ++k) { 426 pw.println(" Session:"); 427 sessionSet.valueAt(k).dump(pw, " "); 428 } 429 } 430 } 431 } 432 } 433 logPerformanceHintSessionAtom(int uid, long sessionId, long targetDuration, int[] tids)434 private void logPerformanceHintSessionAtom(int uid, long sessionId, 435 long targetDuration, int[] tids) { 436 FrameworkStatsLog.write(FrameworkStatsLog.PERFORMANCE_HINT_SESSION_REPORTED, uid, 437 sessionId, targetDuration, tids.length); 438 } 439 } 440 441 @VisibleForTesting 442 final class AppHintSession extends IHintSession.Stub implements IBinder.DeathRecipient { 443 protected final int mUid; 444 protected final int mPid; 445 protected int[] mThreadIds; 446 protected final IBinder mToken; 447 protected long mHalSessionPtr; 448 protected long mTargetDurationNanos; 449 protected boolean mUpdateAllowed; 450 protected int[] mNewThreadIds; 451 AppHintSession( int uid, int pid, int[] threadIds, IBinder token, long halSessionPtr, long durationNanos)452 protected AppHintSession( 453 int uid, int pid, int[] threadIds, IBinder token, 454 long halSessionPtr, long durationNanos) { 455 mUid = uid; 456 mPid = pid; 457 mToken = token; 458 mThreadIds = threadIds; 459 mHalSessionPtr = halSessionPtr; 460 mTargetDurationNanos = durationNanos; 461 mUpdateAllowed = true; 462 updateHintAllowed(); 463 try { 464 token.linkToDeath(this, 0); 465 } catch (RemoteException e) { 466 mNativeWrapper.halCloseHintSession(mHalSessionPtr); 467 throw new IllegalStateException("Client already dead", e); 468 } 469 } 470 471 @VisibleForTesting updateHintAllowed()472 boolean updateHintAllowed() { 473 synchronized (mLock) { 474 final boolean allowed = mUidObserver.isUidForeground(mUid); 475 if (allowed && !mUpdateAllowed) resume(); 476 if (!allowed && mUpdateAllowed) pause(); 477 mUpdateAllowed = allowed; 478 return mUpdateAllowed; 479 } 480 } 481 482 @Override updateTargetWorkDuration(long targetDurationNanos)483 public void updateTargetWorkDuration(long targetDurationNanos) { 484 synchronized (mLock) { 485 if (mHalSessionPtr == 0 || !updateHintAllowed()) { 486 return; 487 } 488 Preconditions.checkArgument(targetDurationNanos > 0, "Expected" 489 + " the target duration to be greater than 0."); 490 mNativeWrapper.halUpdateTargetWorkDuration(mHalSessionPtr, targetDurationNanos); 491 mTargetDurationNanos = targetDurationNanos; 492 } 493 } 494 495 @Override reportActualWorkDuration(long[] actualDurationNanos, long[] timeStampNanos)496 public void reportActualWorkDuration(long[] actualDurationNanos, long[] timeStampNanos) { 497 synchronized (mLock) { 498 if (mHalSessionPtr == 0 || !updateHintAllowed()) { 499 return; 500 } 501 Preconditions.checkArgument(actualDurationNanos.length != 0, "the count" 502 + " of hint durations shouldn't be 0."); 503 Preconditions.checkArgument(actualDurationNanos.length == timeStampNanos.length, 504 "The length of durations and timestamps should be the same."); 505 for (int i = 0; i < actualDurationNanos.length; i++) { 506 if (actualDurationNanos[i] <= 0) { 507 throw new IllegalArgumentException( 508 String.format("durations[%d]=%d should be greater than 0", 509 i, actualDurationNanos[i])); 510 } 511 } 512 mNativeWrapper.halReportActualWorkDuration(mHalSessionPtr, actualDurationNanos, 513 timeStampNanos); 514 } 515 } 516 517 /** TODO: consider monitor session threads and close session if any thread is dead. */ 518 @Override close()519 public void close() { 520 synchronized (mLock) { 521 if (mHalSessionPtr == 0) return; 522 mNativeWrapper.halCloseHintSession(mHalSessionPtr); 523 mHalSessionPtr = 0; 524 mToken.unlinkToDeath(this, 0); 525 ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(mUid); 526 if (tokenMap == null) { 527 Slogf.w(TAG, "UID %d is not present in active session map", mUid); 528 return; 529 } 530 ArraySet<AppHintSession> sessionSet = tokenMap.get(mToken); 531 if (sessionSet == null) { 532 Slogf.w(TAG, "Token %s is not present in token map", mToken.toString()); 533 return; 534 } 535 sessionSet.remove(this); 536 if (sessionSet.isEmpty()) tokenMap.remove(mToken); 537 if (tokenMap.isEmpty()) mActiveSessions.remove(mUid); 538 } 539 } 540 541 @Override sendHint(@erformanceHintManager.Session.Hint int hint)542 public void sendHint(@PerformanceHintManager.Session.Hint int hint) { 543 synchronized (mLock) { 544 if (mHalSessionPtr == 0 || !updateHintAllowed()) { 545 return; 546 } 547 Preconditions.checkArgument(hint >= 0, "the hint ID the hint value should be" 548 + " greater than zero."); 549 mNativeWrapper.halSendHint(mHalSessionPtr, hint); 550 } 551 } 552 setThreads(@onNull int[] tids)553 public void setThreads(@NonNull int[] tids) { 554 synchronized (mLock) { 555 if (mHalSessionPtr == 0) { 556 return; 557 } 558 if (tids.length == 0) { 559 throw new IllegalArgumentException("Thread id list can't be empty."); 560 } 561 final int callingUid = Binder.getCallingUid(); 562 final int callingTgid = Process.getThreadGroupLeader(Binder.getCallingPid()); 563 final long identity = Binder.clearCallingIdentity(); 564 try { 565 if (!checkTidValid(callingUid, callingTgid, tids)) { 566 throw new SecurityException("Some tid doesn't belong to the application."); 567 } 568 } finally { 569 Binder.restoreCallingIdentity(identity); 570 } 571 if (!updateHintAllowed()) { 572 Slogf.v(TAG, "update hint not allowed, storing tids."); 573 mNewThreadIds = tids; 574 return; 575 } 576 mNativeWrapper.halSetThreads(mHalSessionPtr, tids); 577 mThreadIds = tids; 578 } 579 } 580 getThreadIds()581 public int[] getThreadIds() { 582 return mThreadIds; 583 } 584 onProcStateChanged()585 private void onProcStateChanged() { 586 updateHintAllowed(); 587 } 588 pause()589 private void pause() { 590 synchronized (mLock) { 591 if (mHalSessionPtr == 0) return; 592 mNativeWrapper.halPauseHintSession(mHalSessionPtr); 593 } 594 } 595 resume()596 private void resume() { 597 synchronized (mLock) { 598 if (mHalSessionPtr == 0) return; 599 mNativeWrapper.halResumeHintSession(mHalSessionPtr); 600 if (mNewThreadIds != null) { 601 mNativeWrapper.halSetThreads(mHalSessionPtr, mNewThreadIds); 602 mThreadIds = mNewThreadIds; 603 mNewThreadIds = null; 604 } 605 } 606 } 607 dump(PrintWriter pw, String prefix)608 private void dump(PrintWriter pw, String prefix) { 609 synchronized (mLock) { 610 pw.println(prefix + "SessionPID: " + mPid); 611 pw.println(prefix + "SessionUID: " + mUid); 612 pw.println(prefix + "SessionTIDs: " + Arrays.toString(mThreadIds)); 613 pw.println(prefix + "SessionTargetDurationNanos: " + mTargetDurationNanos); 614 pw.println(prefix + "SessionAllowed: " + updateHintAllowed()); 615 } 616 } 617 618 @Override binderDied()619 public void binderDied() { 620 close(); 621 } 622 623 } 624 } 625