1 /** 2 * Copyright (C) 2024 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 package com.android.telephony.imsmedia; 17 18 import android.content.Context; 19 import android.os.PowerManager; 20 import android.telephony.imsmedia.RtpConfig; 21 22 import androidx.annotation.VisibleForTesting; 23 24 import com.android.telephony.imsmedia.util.Log; 25 26 import java.io.PrintWriter; 27 import java.util.HashSet; 28 import java.util.Iterator; 29 30 /** 31 * WakeLockManager makes sure that the process is not suspended when the device switches to 32 * doze mode by acquiring wake lock based on media direction of audio, video and text sessions. 33 */ 34 public class WakeLockManager { 35 private static final String TAG = "WakeLockManager"; 36 private static final String WAKELOCK_TAG = "imsmedia.insession_lock"; 37 private static WakeLockManager sWakeLockManager; 38 39 @VisibleForTesting 40 protected PowerManager.WakeLock mWakeLock; 41 // Sessions for which lock is acquired. 42 private HashSet<Integer> mWakeLockAcquiredSessions = new HashSet<>(); 43 WakeLockManager()44 private WakeLockManager() { 45 Context context = ImsMediaApplication.getAppContext(); 46 47 if (context != null) { 48 PowerManager powerManager = context.getSystemService(PowerManager.class); 49 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); 50 51 // Wake locks are reference counted by default. Still we want to make sure it's true. 52 mWakeLock.setReferenceCounted(true); 53 Log.d(TAG, "WakeLockManager - initialized. Wakelock:" + mWakeLock.toString()); 54 } else { 55 Log.e(TAG, "WakeLockManager not initialized. Context is null"); 56 } 57 } 58 59 /** 60 * Get the instance of WakeLockManager 61 * @return instance of WakeLockManager 62 */ getInstance()63 public static WakeLockManager getInstance() { 64 if (sWakeLockManager == null) { 65 sWakeLockManager = new WakeLockManager(); 66 } 67 68 return sWakeLockManager; 69 } 70 71 /** 72 * Acquires wake lock by incrementing the reference counter of WakeLock. 73 * WakeLockManager should be initialized before calling. 74 */ acquireLock()75 private void acquireLock() { 76 if (mWakeLock != null) { 77 mWakeLock.acquire(); 78 Log.d(TAG, "acquireLock. " + mWakeLock.toString()); 79 return; 80 } 81 Log.e(TAG, "acquireLock - WakeLockManager not initialized"); 82 } 83 84 /** 85 * Releases wake lock by decrementing the reference counter of WakeLock. 86 * WakeLockManager should be initialized before calling. 87 */ releaseLock()88 private void releaseLock() { 89 if (mWakeLock != null && mWakeLock.isHeld()) { 90 mWakeLock.release(); 91 Log.d(TAG, "releaseLock. " + mWakeLock.toString()); 92 return; 93 } 94 95 if (mWakeLock == null) { 96 Log.e(TAG, "releaseLock - WakeLockManager not initialized"); 97 } 98 } 99 100 /** 101 * Releases all wake locks by decrementing the reference counter to zero. 102 * WakeLockManager should be initialized before calling. 103 */ cleanup()104 public synchronized void cleanup() { 105 Log.d(TAG, "cleanup"); 106 if (mWakeLockAcquiredSessions.size() > 0) { 107 Iterator<Integer> iterator = mWakeLockAcquiredSessions.iterator(); 108 while (iterator.hasNext()) { 109 Integer sessionId = iterator.next(); 110 Log.e(TAG, "LEAKED session-id: " + sessionId); 111 } 112 113 mWakeLockAcquiredSessions.clear(); 114 } 115 116 if (mWakeLock != null && mWakeLock.isHeld()) { 117 Log.e(TAG, "LEAKED wakelock: " + mWakeLock); 118 119 // Release leaked wakelock 120 while (mWakeLock.isHeld()) { 121 mWakeLock.release(); 122 } 123 } 124 } 125 126 /** 127 * Helper method used by all session classes (audio, video and text) to acquire or release 128 * wake lock based on session media direction. 129 * 130 * @param sessionId Id of the session 131 * @param mediaDirection current media direction of the session. 132 */ manageWakeLockOnMediaDirectionUpdate( int sessionId, final @RtpConfig.MediaDirection int mediaDirection)133 public synchronized void manageWakeLockOnMediaDirectionUpdate( 134 int sessionId, final @RtpConfig.MediaDirection int mediaDirection) { 135 try { 136 boolean wakeLockAcquired = mWakeLockAcquiredSessions.contains(sessionId); 137 Log.dc(TAG, "manageWakeLockOnMediaDirectionUpdate - SessionId:" + sessionId 138 + ", mediaDirection:" + mediaDirection); 139 140 if (wakeLockAcquired && mediaDirection == RtpConfig.MEDIA_DIRECTION_NO_FLOW) { 141 releaseLock(); 142 mWakeLockAcquiredSessions.remove(sessionId); 143 144 if (mWakeLock.isHeld()) { 145 Log.d(TAG, "Wakelock still held for other sessions. SessionCount:" 146 + mWakeLockAcquiredSessions.size() 147 + ", Wakelock:" + mWakeLock.toString()); 148 149 if (mWakeLockAcquiredSessions.size() <= 0) { 150 cleanup(); 151 } 152 } 153 } else if (!wakeLockAcquired && mediaDirection != RtpConfig.MEDIA_DIRECTION_NO_FLOW) { 154 acquireLock(); 155 mWakeLockAcquiredSessions.add(sessionId); 156 } /* else { 157 if (wakeLockAcquired && mediaDirection != RtpConfig.MEDIA_DIRECTION_NO_FLOW) { 158 * No operation as Lock is already acquired. 159 * Lock is acquired only once per session. 160 } 161 if (!wakeLockAcquired && mediaDirection == RtpConfig.MEDIA_DIRECTION_NO_FLOW) { 162 * No need to acquire lock as media is not flowing. 163 * Example: During VoLTE calls, audio stream is processed in Modem and 164 AP can goto sleep. 165 * Session are configured and ready in this state to handle handover from VoLTE 166 to WiFi. 167 } 168 }*/ 169 } catch (Exception e) { 170 Log.e(TAG, "Exception in manageWakeLockOnMediaDirectionUpdate:" + e.getMessage()); 171 e.printStackTrace(); 172 } 173 } 174 dump(PrintWriter writer)175 void dump(PrintWriter writer) { 176 writer.println("\n--- WakeLockManager ---\n"); 177 writer.println(mWakeLock.toString()); 178 writer.println("Sessions holding wakelock:\n\tCount: " + mWakeLockAcquiredSessions.size()); 179 Iterator<Integer> iterator = mWakeLockAcquiredSessions.iterator(); 180 while (iterator.hasNext()) { 181 writer.println("\tSession Id:" + iterator.next()); 182 } 183 writer.println("\n--- END : WakeLockManager ---\n"); 184 } 185 } 186