Merge changes from topic "imsmedia-log-optimization" into main

* changes:
  Implementation for logging level configuration in ImsMediaService
  Add unit test cases for Log class
  disable identified debug logs in production builds in ImsMediaService
  Add customized Logging support in Imsmedia
diff --git a/service/src/com/android/telephony/imsmedia/AudioListener.java b/service/src/com/android/telephony/imsmedia/AudioListener.java
index 468436d..5fc9f39 100644
--- a/service/src/com/android/telephony/imsmedia/AudioListener.java
+++ b/service/src/com/android/telephony/imsmedia/AudioListener.java
@@ -23,7 +23,8 @@
 import android.telephony.imsmedia.AudioConfig;
 import android.telephony.imsmedia.MediaQualityStatus;
 import android.telephony.imsmedia.RtpReceptionStats;
-import android.util.Log;
+
+import com.android.telephony.imsmedia.util.Log;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -69,7 +70,7 @@
     @Override
     public void onMessage(final Parcel parcel) {
         final int event = parcel.readInt();
-        Log.d(TAG, "onMessage() -" + AudioListener.this + ", event=" + event);
+        Log.dc(TAG, "onMessage() -" + AudioListener.this + ", event=" + event);
         switch (event) {
             case AudioSession.EVENT_OPEN_SESSION_SUCCESS:
                 final int sessionId = parcel.readInt();
diff --git a/service/src/com/android/telephony/imsmedia/AudioLocalSession.java b/service/src/com/android/telephony/imsmedia/AudioLocalSession.java
index 5d4facf..c36cda1 100644
--- a/service/src/com/android/telephony/imsmedia/AudioLocalSession.java
+++ b/service/src/com/android/telephony/imsmedia/AudioLocalSession.java
@@ -20,7 +20,8 @@
 import android.telephony.ims.RtpHeaderExtension;
 import android.telephony.imsmedia.AudioConfig;
 import android.telephony.imsmedia.MediaQualityThreshold;
-import android.util.Log;
+
+import com.android.telephony.imsmedia.util.Log;
 
 import java.util.List;
 
@@ -46,7 +47,7 @@
 
     /** Returns the unique session identifier */
     public int getSessionId() {
-        Log.d(TAG, "getSessionId");
+        Log.dc(TAG, "getSessionId");
         return mSessionId;
     }
 
@@ -74,7 +75,6 @@
      * @param config provides remote end point info and codec details
      */
     public void modifySession(final AudioConfig config) {
-        Log.d(TAG, "modifySession: " + config);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_MODIFY_SESSION);
         if (config != null) {
@@ -90,7 +90,6 @@
      * @param config provides remote end point info and codec details
      */
     public void addConfig(final AudioConfig config) {
-        Log.d(TAG, "addConfig: " + config);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_ADD_CONFIG);
         if (config != null) {
@@ -106,7 +105,6 @@
      * @param config remote config to be deleted
      */
     public void deleteConfig(final AudioConfig config) {
-        Log.d(TAG, "deleteConfig: " + config);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_DELETE_CONFIG);
         if (config != null) {
@@ -125,7 +123,6 @@
      * @param config remote config to be confirmed
      */
     public void confirmConfig(final AudioConfig config) {
-        Log.d(TAG, "confirmConfig: " + config);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_CONFIRM_CONFIG);
         if (config != null) {
@@ -141,7 +138,6 @@
      * @param duration of the key press in milliseconds.
      */
     public void sendDtmf(final char dtmfDigit, final int duration) {
-        Log.d(TAG, "sendDtmf: digit= " + dtmfDigit + ", duration=" + duration);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_SEND_DTMF);
         parcel.writeByte((byte)dtmfDigit);
@@ -155,7 +151,6 @@
      * @param extensions List of RTP header extensions to be transmitted
      */
     public void sendHeaderExtension(final List<RtpHeaderExtension> extensions) {
-        Log.d(TAG, "sendHeaderExtension, extension=" + extensions);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_SEND_RTP_HDR_EXTN);
         parcel.writeInt(extensions.size());
@@ -172,7 +167,6 @@
      * @param threshold media quality thresholds for various quality parameters
      */
     public void setMediaQualityThreshold(final MediaQualityThreshold threshold) {
-        Log.d(TAG, "setMediaQualityThreshold: " + threshold);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_SET_MEDIA_QUALITY_THRESHOLD);
         if (threshold != null) {
@@ -188,7 +182,6 @@
      * @param intervalMs The interval of the time in milliseconds of the rtp reception notification
      */
     public void requestRtpReceptionStats(final int intervalMs) {
-        Log.d(TAG, "requestRtpReceptionStats: interval=" + intervalMs);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_REQUEST_RECEPTION_STATS);
         parcel.writeInt(intervalMs);
@@ -202,7 +195,6 @@
      * always positive.
      */
     public void adjustDelay(final int delayMs) {
-        Log.d(TAG, "adjustDelay: delay=" + delayMs);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_ADJUST_DELAY);
         parcel.writeInt(delayMs);
diff --git a/service/src/com/android/telephony/imsmedia/AudioOffloadService.java b/service/src/com/android/telephony/imsmedia/AudioOffloadService.java
index c18f1e3..f7ba4fb 100644
--- a/service/src/com/android/telephony/imsmedia/AudioOffloadService.java
+++ b/service/src/com/android/telephony/imsmedia/AudioOffloadService.java
@@ -24,9 +24,9 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.imsmedia.AudioConfig;
-import android.util.Log;
 
 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * This connects to IImsMedia HAL and invokes all the HAL APIs
diff --git a/service/src/com/android/telephony/imsmedia/AudioService.java b/service/src/com/android/telephony/imsmedia/AudioService.java
index 7cebf90..e84509e 100644
--- a/service/src/com/android/telephony/imsmedia/AudioService.java
+++ b/service/src/com/android/telephony/imsmedia/AudioService.java
@@ -19,9 +19,9 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.telephony.imsmedia.ImsMediaSession;
-import android.util.Log;
 
 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * Audio service for internal AP based RTP stack. This interacts with native library
@@ -90,9 +90,6 @@
             return ImsMediaSession.RESULT_INVALID_PARAM;
         }
 
-        Log.d(LOG_TAG, "openSession: sessionId = " + sessionId
-                + "," + sessionParams.getRtpConfig());
-
         JNIImsMediaService.setListener(sessionId, mListener);
 
         final int socketFdRtp = rtpSockFd.detachFd();
@@ -118,7 +115,7 @@
      * @param sessionId RTP session to be closed.
      */
     public void closeSession(final int sessionId) {
-        Log.d(LOG_TAG, "closeSession");
+        Log.dc(LOG_TAG, "closeSession");
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(AudioSession.CMD_CLOSE_SESSION);
         sendRequest(sessionId, parcel);
diff --git a/service/src/com/android/telephony/imsmedia/AudioSession.java b/service/src/com/android/telephony/imsmedia/AudioSession.java
index cdac749..3208530 100644
--- a/service/src/com/android/telephony/imsmedia/AudioSession.java
+++ b/service/src/com/android/telephony/imsmedia/AudioSession.java
@@ -30,14 +30,14 @@
 import android.telephony.imsmedia.ImsMediaSession;
 import android.telephony.imsmedia.MediaQualityStatus;
 import android.telephony.imsmedia.MediaQualityThreshold;
-import android.telephony.imsmedia.RtpReceptionStats;
-import android.util.Log;
 import android.telephony.imsmedia.RtpConfig;
+import android.telephony.imsmedia.RtpReceptionStats;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
+import com.android.telephony.imsmedia.util.Log;
 
 import java.util.List;
 import java.util.stream.Collectors;
@@ -167,7 +167,7 @@
 
     @Override
     public void modifySession(AudioConfig config) {
-        Log.d(TAG, "modifySession: " + config);
+        Log.d(TAG, "modifySession: " + Log.hidePii(String.valueOf(config)));
         Utils.sendMessage(mHandler, CMD_MODIFY_SESSION, config);
         WakeLockManager.getInstance().manageWakeLockOnMediaDirectionUpdate(
                 mSessionId, config.getMediaDirection());
@@ -175,48 +175,48 @@
 
     @Override
     public void addConfig(AudioConfig config) {
-        Log.d(TAG, "addConfig: " + config);
+        Log.d(TAG, "addConfig: " + Log.hidePii(String.valueOf(config)));
         Utils.sendMessage(mHandler, CMD_ADD_CONFIG, config);
     }
 
     @Override
     public void deleteConfig(AudioConfig config) {
-        Log.d(TAG, "deleteConfig: " + config);
+        Log.d(TAG, "deleteConfig: " + Log.hidePii(String.valueOf(config)));
         Utils.sendMessage(mHandler, CMD_DELETE_CONFIG, config);
     }
 
     @Override
     public void confirmConfig(AudioConfig config) {
-        Log.d(TAG, "confirmConfig: " + config);
+        Log.d(TAG, "confirmConfig: " + Log.hidePii(String.valueOf(config)));
         Utils.sendMessage(mHandler, CMD_CONFIRM_CONFIG, config);
     }
 
     @Override
     public void sendDtmf(char digit, int duration) {
-        Log.d(TAG, "sendDtmf: digit=" + digit + ",duration=" + duration);
+        Log.dc(TAG, "sendDtmf: digit=" + digit + ",duration=" + duration);
         Utils.sendMessage(mHandler, CMD_SEND_DTMF, duration, Utils.UNUSED, digit);
     }
 
     @Override
     public void startDtmf(char digit) {
-        Log.d(TAG, "startDtmf: digit=" + digit);
+        Log.dc(TAG, "startDtmf: digit=" + digit);
         Utils.sendMessage(mHandler, CMD_START_DTMF, digit);
     }
 
     @Override
     public void stopDtmf() {
-        Log.d(TAG, "stopDtmf");
+        Log.dc(TAG, "stopDtmf");
         Utils.sendMessage(mHandler, CMD_STOP_DTMF);
     }
     @Override
     public void sendHeaderExtension(List<RtpHeaderExtension> extensions) {
-        Log.d(TAG, "sendHeaderExtension");
+        Log.d(TAG, "sendHeaderExtension" + Log.hidePii(String.valueOf(extensions)));
         Utils.sendMessage(mHandler, CMD_SEND_RTP_HDR_EXTN, extensions);
     }
 
     @Override
     public void setMediaQualityThreshold(MediaQualityThreshold threshold) {
-        Log.d(TAG, "setMediaQualityThreshold: " + threshold);
+        Log.d(TAG, "setMediaQualityThreshold: " + Log.hidePii(String.valueOf(threshold)));
         Utils.sendMessage(mHandler, CMD_SET_MEDIA_QUALITY_THRESHOLD, threshold);
     }
 
@@ -234,19 +234,16 @@
 
     @Override
     public void onOpenSessionSuccess(Object session) {
-        Log.d(TAG, "onOpenSessionSuccess");
         Utils.sendMessage(mHandler, EVENT_OPEN_SESSION_SUCCESS, session);
     }
 
     @Override
     public void onOpenSessionFailure(int error) {
-        Log.d(TAG, "onOpenSessionFailure: error=" + error);
         Utils.sendMessage(mHandler, EVENT_OPEN_SESSION_FAILURE, error);
     }
 
     @Override
     public void onSessionClosed() {
-        Log.d(TAG, "onSessionClosed");
         Utils.sendMessage(mHandler, EVENT_SESSION_CLOSED);
     }
 
@@ -264,7 +261,7 @@
 
         @Override
         public void handleMessage (Message msg) {
-            Log.d(TAG, "handleMessage() -" + AudioSessionHandler.this + ", " + msg.what);
+            Log.dc(TAG, "handleMessage() -" + AudioSessionHandler.this + ", " + msg.what);
             switch(msg.what) {
                 case CMD_OPEN_SESSION:
                     handleOpenSession((OpenSessionParams)msg.obj);
@@ -362,7 +359,6 @@
     }
 
     private void handleCloseSession() {
-        Log.d(TAG, "handleCloseSession");
         if (isAudioOffload()) {
             mOffloadService.closeSession(mSessionId);
         } else {
diff --git a/service/src/com/android/telephony/imsmedia/ImsMediaApplication.java b/service/src/com/android/telephony/imsmedia/ImsMediaApplication.java
index ad6be77..f40af6c 100644
--- a/service/src/com/android/telephony/imsmedia/ImsMediaApplication.java
+++ b/service/src/com/android/telephony/imsmedia/ImsMediaApplication.java
@@ -17,7 +17,8 @@
 
 import android.app.Application;
 import android.content.Context;
-import android.util.Log;
+
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * Extension of Application class to catch unhandled exception and release wake lock.
diff --git a/service/src/com/android/telephony/imsmedia/ImsMediaController.java b/service/src/com/android/telephony/imsmedia/ImsMediaController.java
index f99d00e..46e77de 100644
--- a/service/src/com/android/telephony/imsmedia/ImsMediaController.java
+++ b/service/src/com/android/telephony/imsmedia/ImsMediaController.java
@@ -33,11 +33,11 @@
 import android.telephony.imsmedia.ImsMediaSession;
 import android.telephony.imsmedia.RtpConfig;
 import android.telephony.imsmedia.VideoConfig;
-import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.util.DumpUtils;
 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
+import com.android.telephony.imsmedia.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -63,7 +63,7 @@
 
             IMediaSession session;
             Log.d(TAG, "openSession: sessionId = " + sessionId
-                    + ", type=" + sessionType + "," + rtpConfig);
+                    + ", type=" + sessionType + "," + Log.hidePii(String.valueOf(rtpConfig)));
             synchronized (mSessions) {
                 switch (sessionType) {
                     case ImsMediaSession.SESSION_TYPE_AUDIO:
@@ -116,7 +116,7 @@
         public void generateVideoSprop(VideoConfig[] videoConfigList, IBinder callback) {
 
             if (videoConfigList == null || callback == null) {
-                Log.d(TAG, "[SPROP] Invalid params");
+                Log.w(TAG, "[SPROP] Invalid params");
                 return;
             }
 
@@ -161,7 +161,7 @@
                 mSessions.clear();
             }
         } catch (Exception e) {
-            Log.d(TAG, "onUnbind: e=" + e);
+            Log.e(TAG, "onUnbind: e = " + e);
         }
         return true;
     }
@@ -208,11 +208,13 @@
 
     public class OpenSessionCallback {
         public void onOpenSessionSuccess(int sessionId, Object session) {
+            Log.d(TAG, "onOpenSessionSuccess: sessionId = " + sessionId);
             getSession(sessionId).onOpenSessionSuccess(session);
         }
 
         public void onOpenSessionFailure(int sessionId, int error) {
             synchronized (mSessions) {
+                Log.e(TAG, "onOpenSessionFailure: sessionId = " + sessionId + " error = " + error);
                 getSession(sessionId).onOpenSessionFailure(error);
                 mSessions.remove(sessionId);
             }
@@ -225,8 +227,8 @@
          */
         public void onSessionClosed(int sessionId) {
             synchronized (mSessions) {
-                getSession(sessionId).onSessionClosed();
                 Log.d(TAG, "onSessionClosed: sessionId = " + sessionId);
+                getSession(sessionId).onSessionClosed();
                 mSessions.remove(sessionId);
 
                 if (mSessions.size() <= 0) {
diff --git a/service/src/com/android/telephony/imsmedia/JNIImsMediaService.java b/service/src/com/android/telephony/imsmedia/JNIImsMediaService.java
index ff072f6..e5c442c 100644
--- a/service/src/com/android/telephony/imsmedia/JNIImsMediaService.java
+++ b/service/src/com/android/telephony/imsmedia/JNIImsMediaService.java
@@ -20,11 +20,12 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.util.ArrayMap;
-import android.util.Log;
 import android.view.Surface;
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.telephony.imsmedia.util.Log;
+
 /** JNI interface class to send message to libimsmediajni */
 public class JNIImsMediaService {
     private static final String TAG = "JNIImsMediaService";
@@ -163,7 +164,7 @@
      * @return 1 if it is success to send data, -1 when it fails
      */
     public static int sendData2Java(final int sessionId, final byte[] baData) {
-        Log.d(TAG, "sendData2Java() - sessionId=" + sessionId);
+        Log.dc(TAG, "sendData2Java() - sessionId=" + sessionId);
         JNIImsMediaListener listener = getListener(sessionId);
         if (listener == null) {
             Log.e(TAG, "No listener :: sessionId=" + sessionId);
diff --git a/service/src/com/android/telephony/imsmedia/TextListener.java b/service/src/com/android/telephony/imsmedia/TextListener.java
index 2c7d620..dcb9c69 100644
--- a/service/src/com/android/telephony/imsmedia/TextListener.java
+++ b/service/src/com/android/telephony/imsmedia/TextListener.java
@@ -19,7 +19,8 @@
 import android.os.Handler;
 import android.os.Parcel;
 import android.telephony.imsmedia.TextConfig;
-import android.util.Log;
+
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * Text listener to process JNI messages from local AP based RTP stack
@@ -62,7 +63,7 @@
     @Override
     public void onMessage(final Parcel parcel) {
         final int event = parcel.readInt();
-        Log.d(TAG, "onMessage() -" + TextListener.this + ", event=" + event);
+        Log.dc(TAG, "onMessage() -" + TextListener.this + ", event=" + event);
         switch (event) {
             case TextSession.EVENT_OPEN_SESSION_SUCCESS:
                 final int sessionId = parcel.readInt();
diff --git a/service/src/com/android/telephony/imsmedia/TextLocalSession.java b/service/src/com/android/telephony/imsmedia/TextLocalSession.java
index f0f0ebf..8c39893 100644
--- a/service/src/com/android/telephony/imsmedia/TextLocalSession.java
+++ b/service/src/com/android/telephony/imsmedia/TextLocalSession.java
@@ -19,7 +19,8 @@
 import android.os.Parcel;
 import android.telephony.imsmedia.MediaQualityThreshold;
 import android.telephony.imsmedia.TextConfig;
-import android.util.Log;
+
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * Text session implementation for internal AP based RTP stack. This handles all API calls from
@@ -43,7 +44,7 @@
 
     /** Returns the unique session identifier */
     public int getSessionId() {
-        Log.d(TAG, "getSessionId");
+        Log.dc(TAG, "getSessionId");
         return mSessionId;
     }
 
@@ -74,7 +75,6 @@
      * @param config provides remote end point info and codec details
      */
     public void modifySession(final TextConfig config) {
-        Log.d(TAG, "modifySession: " + config);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(TextSession.CMD_MODIFY_SESSION);
         if (config != null) {
@@ -90,7 +90,6 @@
      * @param threshold media quality thresholds for various quality parameters
      */
     public void setMediaQualityThreshold(final MediaQualityThreshold threshold) {
-        Log.d(TAG, "setMediaQualityThreshold: " + threshold);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(TextSession.CMD_SET_MEDIA_QUALITY_THRESHOLD);
         if (threshold != null) {
@@ -105,7 +104,6 @@
      * @param text The text string
      */
     public void sendRtt(String text) {
-        Log.d(TAG, "sendRtt");
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(TextSession.CMD_SEND_RTT);
         parcel.writeString(text);
diff --git a/service/src/com/android/telephony/imsmedia/TextService.java b/service/src/com/android/telephony/imsmedia/TextService.java
index e7e524f..f01f265 100644
--- a/service/src/com/android/telephony/imsmedia/TextService.java
+++ b/service/src/com/android/telephony/imsmedia/TextService.java
@@ -19,9 +19,9 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.telephony.imsmedia.ImsMediaSession;
-import android.util.Log;
 
 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * Text service for internal AP based RTP stack. This interacts with native library to open/close
@@ -89,9 +89,6 @@
             return ImsMediaSession.RESULT_INVALID_PARAM;
         }
 
-        Log.d(LOG_TAG, "openSession: sessionId = " + sessionId
-                + "," + sessionParams.getRtpConfig());
-
         JNIImsMediaService.setListener(sessionId, mListener);
 
         final int socketFdRtp = rtpSockFd.detachFd();
@@ -116,7 +113,7 @@
      * @param sessionId The session id to be closed.
      */
     public void closeSession(final int sessionId) {
-        Log.d(LOG_TAG, "closeSession");
+        Log.dc(LOG_TAG, "closeSession");
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(TextSession.CMD_CLOSE_SESSION);
         sendRequest(sessionId, parcel);
diff --git a/service/src/com/android/telephony/imsmedia/TextSession.java b/service/src/com/android/telephony/imsmedia/TextSession.java
index ff791ac..978b992 100644
--- a/service/src/com/android/telephony/imsmedia/TextSession.java
+++ b/service/src/com/android/telephony/imsmedia/TextSession.java
@@ -27,12 +27,12 @@
 import android.telephony.imsmedia.MediaQualityThreshold;
 import android.telephony.imsmedia.RtpConfig;
 import android.telephony.imsmedia.TextConfig;
-import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * Text session binder implementation which handles all text session APIs from the text service.
@@ -118,7 +118,7 @@
 
     @Override
     public void modifySession(TextConfig config) {
-        Log.d(TAG, "modifySession: " + config);
+        Log.d(TAG, "modifySession: " + Log.hidePii(String.valueOf(config)));
         Utils.sendMessage(mHandler, CMD_MODIFY_SESSION, config);
         WakeLockManager.getInstance().manageWakeLockOnMediaDirectionUpdate(
                 mSessionId, config.getMediaDirection());
@@ -126,31 +126,28 @@
 
     @Override
     public void setMediaQualityThreshold(MediaQualityThreshold threshold) {
-        Log.d(TAG, "setMediaQualityThreshold: " + threshold);
+        Log.d(TAG, "setMediaQualityThreshold: " + Log.hidePii(String.valueOf(threshold)));
         Utils.sendMessage(mHandler, CMD_SET_MEDIA_QUALITY_THRESHOLD, threshold);
     }
 
     @Override
     public void sendRtt(String text) {
-        Log.d(TAG, "sendRtt: ");
+        Log.dc(TAG, "sendRtt: ");
         Utils.sendMessage(mHandler, CMD_SEND_RTT, text);
     }
 
     @Override
     public void onOpenSessionSuccess(Object session) {
-        Log.d(TAG, "onOpenSessionSuccess");
         Utils.sendMessage(mHandler, EVENT_OPEN_SESSION_SUCCESS, session);
     }
 
     @Override
     public void onOpenSessionFailure(int error) {
-        Log.d(TAG, "onOpenSessionFailure: error=" + error);
         Utils.sendMessage(mHandler, EVENT_OPEN_SESSION_FAILURE, error);
     }
 
     @Override
     public void onSessionClosed() {
-        Log.d(TAG, "onSessionClosed");
         Utils.sendMessage(mHandler, EVENT_SESSION_CLOSED);
     }
 
@@ -164,7 +161,7 @@
 
         @Override
         public void handleMessage(Message msg) {
-            Log.d(TAG, "handleMessage() -" + TextSessionHandler.this + ", " + msg.what);
+            Log.dc(TAG, "handleMessage() -" + TextSessionHandler.this + ", " + msg.what);
             switch (msg.what) {
                 case CMD_OPEN_SESSION:
                     handleOpenSession((OpenSessionParams) msg.obj);
@@ -214,7 +211,6 @@
     }
 
     private void handleCloseSession() {
-        Log.d(TAG, "handleCloseSession");
         mTextService.closeSession(mSessionId);
     }
 
diff --git a/service/src/com/android/telephony/imsmedia/VideoListener.java b/service/src/com/android/telephony/imsmedia/VideoListener.java
index 3212297..65d444a 100644
--- a/service/src/com/android/telephony/imsmedia/VideoListener.java
+++ b/service/src/com/android/telephony/imsmedia/VideoListener.java
@@ -19,7 +19,8 @@
 import android.os.Handler;
 import android.os.Parcel;
 import android.telephony.imsmedia.VideoConfig;
-import android.util.Log;
+
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * Video listener to process JNI messages from local AP based RTP stack
@@ -62,7 +63,7 @@
     @Override
     public void onMessage(final Parcel parcel) {
         final int event = parcel.readInt();
-        Log.d(TAG, "onMessage() -" + VideoListener.this + ", event=" + event);
+        Log.dc(TAG, "onMessage() -" + VideoListener.this + ", event=" + event);
         switch (event) {
             case VideoSession.EVENT_OPEN_SESSION_SUCCESS:
                 final int sessionId = parcel.readInt();
diff --git a/service/src/com/android/telephony/imsmedia/VideoLocalSession.java b/service/src/com/android/telephony/imsmedia/VideoLocalSession.java
index 743ed8b..d50fad0 100644
--- a/service/src/com/android/telephony/imsmedia/VideoLocalSession.java
+++ b/service/src/com/android/telephony/imsmedia/VideoLocalSession.java
@@ -20,9 +20,10 @@
 import android.telephony.ims.RtpHeaderExtension;
 import android.telephony.imsmedia.MediaQualityThreshold;
 import android.telephony.imsmedia.VideoConfig;
-import android.util.Log;
 import android.view.Surface;
 
+import com.android.telephony.imsmedia.util.Log;
+
 import java.util.List;
 
 /** Video session implementation for internal AP based RTP stack. This handles
@@ -45,7 +46,7 @@
 
     /** Returns the unique session identifier */
     public int getSessionId() {
-        Log.d(TAG, "getSessionId");
+        Log.dc(TAG, "getSessionId");
         return mSessionId;
     }
 
@@ -74,7 +75,6 @@
      * @param config provides remote end point info and codec details
      */
     public void modifySession(final VideoConfig config) {
-        Log.d(TAG, "modifySession: " + config);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(VideoSession.CMD_MODIFY_SESSION);
         if (config != null) {
@@ -115,7 +115,6 @@
      *        parameters
      */
     public void setMediaQualityThreshold(final MediaQualityThreshold threshold) {
-        Log.d(TAG, "setMediaQualityThreshold: " + threshold);
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(VideoSession.CMD_SET_MEDIA_QUALITY_THRESHOLD);
         if (threshold != null) {
@@ -130,7 +129,7 @@
      * @param extensions List of RTP header extensions to be transmitted
      */
     public void sendHeaderExtension(final List<RtpHeaderExtension> extensions) {
-        Log.d(TAG, "sendHeaderExtension");
+        Log.dc(TAG, "sendHeaderExtension");
         // TODO: add implementation
     }
 
diff --git a/service/src/com/android/telephony/imsmedia/VideoService.java b/service/src/com/android/telephony/imsmedia/VideoService.java
index 395f516..6de3a2a 100644
--- a/service/src/com/android/telephony/imsmedia/VideoService.java
+++ b/service/src/com/android/telephony/imsmedia/VideoService.java
@@ -19,9 +19,9 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.telephony.imsmedia.ImsMediaSession;
-import android.util.Log;
 
 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * Video service for internal AP based RTP stack. This interacts with native library
@@ -90,9 +90,6 @@
             return ImsMediaSession.RESULT_INVALID_PARAM;
         }
 
-        Log.d(LOG_TAG, "openSession: sessionId = " + sessionId
-                + "," + sessionParams.getRtpConfig());
-
         JNIImsMediaService.setListener(sessionId, mListener);
 
         final int socketFdRtp = rtpSockFd.detachFd();
@@ -118,7 +115,7 @@
      * @param sessionId RTP session to be closed.
      */
     public void closeSession(final int sessionId) {
-        Log.d(LOG_TAG, "closeSession");
+        Log.dc(LOG_TAG, "closeSession");
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(VideoSession.CMD_CLOSE_SESSION);
         sendRequest(sessionId, parcel);
diff --git a/service/src/com/android/telephony/imsmedia/VideoSession.java b/service/src/com/android/telephony/imsmedia/VideoSession.java
index e6ceefd..2e3b6e0 100644
--- a/service/src/com/android/telephony/imsmedia/VideoSession.java
+++ b/service/src/com/android/telephony/imsmedia/VideoSession.java
@@ -28,13 +28,13 @@
 import android.telephony.imsmedia.MediaQualityThreshold;
 import android.telephony.imsmedia.RtpConfig;
 import android.telephony.imsmedia.VideoConfig;
-import android.util.Log;
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
+import com.android.telephony.imsmedia.util.Log;
 
 import java.util.List;
 
@@ -130,7 +130,7 @@
 
     @Override
     public void modifySession(VideoConfig config) {
-        Log.d(TAG, "modifySession: " + config);
+        Log.d(TAG, "modifySession: " + Log.hidePii(String.valueOf(config)));
         Utils.sendMessage(mHandler, CMD_MODIFY_SESSION, config);
         WakeLockManager.getInstance().manageWakeLockOnMediaDirectionUpdate(
                 mSessionId, config.getMediaDirection());
@@ -138,49 +138,46 @@
 
     @Override
     public void setPreviewSurface(Surface surface) {
-        Log.d(TAG, "setPreviewSurface: " + surface);
+        Log.dc(TAG, "setPreviewSurface: " + surface);
         Utils.sendMessage(mHandler, CMD_SET_PREVIEW_SURFACE, surface);
     }
 
     @Override
     public void setDisplaySurface(Surface surface) {
-        Log.d(TAG, "setDisplaySurface: " + surface);
+        Log.dc(TAG, "setDisplaySurface: " + surface);
         Utils.sendMessage(mHandler, CMD_SET_DISPLAY_SURFACE, surface);
     }
 
     @Override
     public void sendHeaderExtension(List<RtpHeaderExtension> extensions) {
-        Log.d(TAG, "sendHeaderExtension");
+        Log.d(TAG, "sendHeaderExtension" + Log.hidePii(String.valueOf(extensions)));
         Utils.sendMessage(mHandler, CMD_SEND_RTP_HDR_EXTN, extensions);
     }
 
     @Override
     public void setMediaQualityThreshold(MediaQualityThreshold threshold) {
-        Log.d(TAG, "setMediaQualityThreshold: " + threshold);
+        Log.d(TAG, "setMediaQualityThreshold: " + Log.hidePii(String.valueOf(threshold)));
         Utils.sendMessage(mHandler, CMD_SET_MEDIA_QUALITY_THRESHOLD, threshold);
     }
 
     @Override
     public void requestVideoDataUsage() {
-        Log.d(TAG, "requestVideoDataUsage: ");
+        Log.dc(TAG, "requestVideoDataUsage: ");
         Utils.sendMessage(mHandler, CMD_REQUEST_VIDEO_DATA_USAGE);
     }
 
     @Override
     public void onOpenSessionSuccess(Object session) {
-        Log.d(TAG, "onOpenSessionSuccess");
         Utils.sendMessage(mHandler, EVENT_OPEN_SESSION_SUCCESS, session);
     }
 
     @Override
     public void onOpenSessionFailure(int error) {
-        Log.d(TAG, "onOpenSessionFailure: error=" + error);
         Utils.sendMessage(mHandler, EVENT_OPEN_SESSION_FAILURE, error);
     }
 
     @Override
     public void onSessionClosed() {
-        Log.d(TAG, "onSessionClosed");
         Utils.sendMessage(mHandler, EVENT_SESSION_CLOSED);
     }
 
@@ -194,7 +191,7 @@
 
         @Override
         public void handleMessage(Message msg) {
-            Log.d(TAG, "handleMessage() -" + VideoSessionHandler.this + ", " + msg.what);
+            Log.dc(TAG, "handleMessage() -" + VideoSessionHandler.this + ", " + msg.what);
             switch(msg.what) {
                 case CMD_OPEN_SESSION:
                     handleOpenSession((OpenSessionParams) msg.obj);
@@ -257,7 +254,6 @@
 
     private void handleOpenSession(OpenSessionParams sessionParams) {
         mVideoListener.setMediaCallback(sessionParams.getCallback());
-        Log.d(TAG, "handleOpenSession");
         int result = mVideoService.openSession(mSessionId, sessionParams);
         if (result != ImsMediaSession.RESULT_SUCCESS) {
             handleOpenFailure(result);
@@ -265,7 +261,6 @@
     }
 
     private void handleCloseSession() {
-        Log.d(TAG, "handleCloseSession");
         mVideoService.closeSession(mSessionId);
     }
 
diff --git a/service/src/com/android/telephony/imsmedia/WakeLockManager.java b/service/src/com/android/telephony/imsmedia/WakeLockManager.java
index 33bfcc5..b03ac0f 100644
--- a/service/src/com/android/telephony/imsmedia/WakeLockManager.java
+++ b/service/src/com/android/telephony/imsmedia/WakeLockManager.java
@@ -18,10 +18,11 @@
 import android.content.Context;
 import android.os.PowerManager;
 import android.telephony.imsmedia.RtpConfig;
-import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.telephony.imsmedia.util.Log;
+
 import java.io.PrintWriter;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -133,7 +134,7 @@
             int sessionId, final @RtpConfig.MediaDirection int mediaDirection) {
         try {
             boolean wakeLockAcquired = mWakeLockAcquiredSessions.contains(sessionId);
-            Log.d(TAG, "manageWakeLockOnMediaDirectionUpdate - SessionId:" + sessionId
+            Log.dc(TAG, "manageWakeLockOnMediaDirectionUpdate - SessionId:" + sessionId
                     + ", mediaDirection:" + mediaDirection);
 
             if (wakeLockAcquired && mediaDirection == RtpConfig.MEDIA_DIRECTION_NO_FLOW) {
diff --git a/service/src/com/android/telephony/imsmedia/config/ConfigPreference.java b/service/src/com/android/telephony/imsmedia/config/ConfigPreference.java
index 5374fca..ac64417 100644
--- a/service/src/com/android/telephony/imsmedia/config/ConfigPreference.java
+++ b/service/src/com/android/telephony/imsmedia/config/ConfigPreference.java
@@ -21,13 +21,13 @@
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
-import android.util.Log;
 import android.util.SparseArray;
 import android.view.Window;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.telephony.imsmedia.JNIImsMediaService;
 import com.android.telephony.imsmedia.R;
+import com.android.telephony.imsmedia.util.Log;
 
 /**
  * The configuration of logging and test options for the libimsmedia
@@ -98,7 +98,7 @@
     @SuppressWarnings("deprecation")
     @Override
     public void onCreate(Bundle savedInstanceState) {
-        Log.d(LOG_TAG, "onCreate");
+        Log.dc(LOG_TAG, "onCreate");
 
         super.onCreate(savedInstanceState);
 
@@ -133,7 +133,7 @@
     @Override
     public void onResume() {
         super.onResume();
-        Log.d(LOG_TAG, "onResume");
+        Log.dc(LOG_TAG, "onResume");
         initPreferences();
     }
 
@@ -147,9 +147,9 @@
     }
 
     private void initPreferences() {
-        Log.d(LOG_TAG, "initPreferences");
+        Log.dc(LOG_TAG, "initPreferences");
         for (int i = 0; i < KEY_LIST_PREFERENCES.length; ++i) {
-            Log.d(LOG_TAG, "initPreferences, key=" + KEY_LIST_PREFERENCES[i]);
+            Log.dc(LOG_TAG, "initPreferences, key=" + KEY_LIST_PREFERENCES[i]);
             ListPreference itemList = (ListPreference) findPreference(KEY_LIST_PREFERENCES[i]);
             mListPrefs.put(i, itemList);
             if (itemList != null) {
@@ -161,7 +161,7 @@
         }
 
         for (int i = 0; i < KEY_CHECKBOX_PREFERENCES.length; ++i) {
-            Log.d(LOG_TAG, "initPreferences, key=" + KEY_CHECKBOX_PREFERENCES[i]);
+            Log.dc(LOG_TAG, "initPreferences, key=" + KEY_CHECKBOX_PREFERENCES[i]);
             CheckBoxPreference check =
                     (CheckBoxPreference) findPreference(KEY_CHECKBOX_PREFERENCES[i]);
             mCheckboxPrefs.put(i, check);
@@ -175,8 +175,9 @@
             }
         }
 
-        Log.d(LOG_TAG, "initPreferences, LogMode=" + mLogMode + ", DebugLogMode=" + mDebugLogMode);
+        Log.dc(LOG_TAG, "initPreferences, LogMode=" + mLogMode + ", DebugLogMode=" + mDebugLogMode);
         JNIImsMediaService.setLogMode(mLogMode, mDebugLogMode);
+        Log.init(mLogMode);
     }
 
     private final class ConfigListItemChangeListener
@@ -189,12 +190,13 @@
         @Override
         public boolean onPreferenceChange(Preference preference, Object newValue) {
             String value = newValue.toString();
-            Log.d(LOG_TAG, "onPreferenceChange: key=" + preference.getKey() + ",value=" + value);
+            Log.dc(LOG_TAG, "onPreferenceChange: key=" + preference.getKey() + ",value=" + value);
             ListPreference itemList = mListPrefs.valueAt(mPrefIndex);
             if (itemList != null) {
                 mLogMode = parseInt(value, 0);
                 itemList.setSummary(value);
                 JNIImsMediaService.setLogMode(mLogMode, mDebugLogMode);
+                Log.setLogLevel(mLogMode);
             }
             return true;
         }
@@ -210,7 +212,7 @@
         @Override
         public boolean onPreferenceChange(Preference preference, Object newValue) {
             String value = newValue.toString();
-            Log.d(LOG_TAG, "onPreferenceChange: key=" + preference.getKey() + ", value=" + value);
+            Log.dc(LOG_TAG, "onPreferenceChange: key=" + preference.getKey() + ", value=" + value);
             boolean boolValue = Boolean.valueOf(value);
             if (boolValue) {
                 mDebugLogMode |= DEBUG_MODE_ARRAY[mPrefIndex];
@@ -218,6 +220,7 @@
                 mDebugLogMode &= ~DEBUG_MODE_ARRAY[mPrefIndex];
             }
             JNIImsMediaService.setLogMode(mLogMode, mDebugLogMode);
+            Log.setLogLevel(mLogMode);
             return true;
         }
     }
diff --git a/service/src/com/android/telephony/imsmedia/util/Log.java b/service/src/com/android/telephony/imsmedia/util/Log.java
new file mode 100644
index 0000000..6e317f7
--- /dev/null
+++ b/service/src/com/android/telephony/imsmedia/util/Log.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2024 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.telephony.imsmedia.util;
+
+import android.annotation.IntDef;
+import android.os.Build;
+import android.support.annotation.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class provides the logging interface and utility methods
+ * that will be helpful when adding the log statement.
+ */
+public final class Log {
+    public static final String TAG = "Log";
+    public static final String HIDDEN = "****";
+    public static final String EMPTY = "(empty)";
+    public static final String NULL = "(null)";
+    /**
+     * Logging options.
+     */
+
+    /**
+     * Priority Level constant for Logging all level logs; use Log.v.
+     */
+    public static final int LOG_LEVEL_ALL = 0;
+
+    /**
+     * Priority Level constant for Logging Verbose logs; use Log.v.
+     */
+    public static final int LOG_LEVEL_VERBOSE = 1;
+
+    /**
+     * Priority Level constant for Logging debug logs; use Log.d.
+     */
+    public static final int LOG_LEVEL_DEBUG = 2;
+
+    /**
+     * Priority Level constant for Logging Info logs; use Log.i.
+     */
+    public static final int LOG_LEVEL_INFO = 3;
+
+    /**
+     * Priority Level constant for Logging Warning logs; use Log.w.
+     */
+    public static final int LOG_LEVEL_WARN = 4;
+
+    /**
+     * Priority Level constant for Logging Error logs; use Log.e.
+     */
+    public static final int LOG_LEVEL_ERROR = 5;
+
+    /** @hide */
+    @IntDef(
+        value = {
+            LOG_LEVEL_ALL,
+            LOG_LEVEL_VERBOSE,
+            LOG_LEVEL_DEBUG,
+            LOG_LEVEL_INFO,
+            LOG_LEVEL_WARN,
+            LOG_LEVEL_ERROR
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LogLevel {}
+
+    // all levels(V / D / I / W / E)
+    public static final int DEFAULT_LOG_LEVEL = LOG_LEVEL_ALL;
+
+    private static int sDebug = -1;
+    private static int sLogLevel = LOG_LEVEL_ALL;
+
+    static {
+        if (Build.IS_DEBUGGABLE) {
+            sDebug = 1;
+            init(DEFAULT_LOG_LEVEL);
+            Log.d(TAG, "All logs are enabled");
+        } else {
+            init(LOG_LEVEL_DEBUG);
+            Log.d(TAG, "Verbose logs are disabled");
+        }
+    }
+
+    /**
+     * Initializes the logging configuration.
+     *
+     * @param logLevel A logging priority level.
+     */
+    public static void init(@LogLevel int logLevel) {
+        setLogLevel(logLevel);
+    }
+
+    /**
+     * Returns whether the debug mode is enabled or not.
+     */
+    public static boolean isDebuggable() {
+        if (sDebug == 1) {
+            return true;
+        } else if (sDebug == -1) {
+            return isLoggable(android.util.Log.DEBUG);
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks whether android logging is enabled for the given log level.
+     *
+     * @param level log level
+     */
+    public static boolean isLoggable(int level) {
+        return android.util.Log.isLoggable(TAG, level);
+    }
+
+    /** Sets the debug mode from the specified flag. */
+    @VisibleForTesting
+    static void setDebuggable(boolean enableDebug) {
+        sDebug = enableDebug ? 1 : 0;
+    }
+
+    /** Returns the log Level. */
+    @VisibleForTesting
+    static int getLogLevel() {
+        return sLogLevel;
+    }
+
+    /** Sets the log Level. */
+    public static void setLogLevel(@LogLevel int logLevel) {
+        sLogLevel = logLevel;
+    }
+
+    /** Checks whether ImsMedia logging is enabled or not for given level. */
+    @VisibleForTesting
+    static boolean isLogEnabled(@LogLevel int level) {
+        if (sLogLevel == LOG_LEVEL_ALL) {
+            // All logs are available as default.
+            return true;
+        }
+        return sLogLevel <= level;
+    }
+
+    /**
+     * Prints the verbose level log.
+     */
+    public static void v(String tag, String msg) {
+        if (isLogEnabled(LOG_LEVEL_VERBOSE)) {
+            android.util.Log.v(tag, msg);
+        }
+    }
+
+    /**
+     * Prints the debug level log.
+     */
+    public static void d(String tag, String msg) {
+        if (isLogEnabled(LOG_LEVEL_DEBUG)) {
+            android.util.Log.d(tag, msg);
+        }
+    }
+
+    /**
+     * Prints the debug level log when the debug mode is enabled.
+     */
+    public static void dc(String tag, String msg) {
+        if (isDebuggable()) {
+            d(tag, msg);
+        }
+    }
+
+    /**
+     * Prints the information level log.
+     */
+    public static void i(String tag, String msg) {
+        if (isLogEnabled(LOG_LEVEL_INFO)) {
+            android.util.Log.i(tag, msg);
+        }
+    }
+
+    /**
+     * Prints the warning level log.
+     */
+    public static void w(String tag, String msg) {
+        if (isLogEnabled(LOG_LEVEL_WARN)) {
+            android.util.Log.w(tag, msg);
+        }
+    }
+
+    /**
+     * Prints the error level log.
+     */
+    public static void e(String tag, String msg) {
+        if (isLogEnabled(LOG_LEVEL_ERROR)) {
+            android.util.Log.e(tag, msg);
+        }
+    }
+
+    /**
+     * Prints the error level log.
+     */
+    public static void e(String tag, String msg, Throwable t) {
+        if (isLogEnabled(LOG_LEVEL_ERROR)) {
+            android.util.Log.e(tag, msg, t);
+        }
+    }
+
+    /**
+     * Hides personal identifiable information logging string if debug mode is disabled.
+     *
+     * @param msg logging string
+     * @return hidden string if debug mode is disabled. Original string otherwise.
+     */
+    public static String hidePii(String msg) {
+        if (msg == null) {
+            return NULL;
+        } else if (msg.isEmpty()) {
+            return EMPTY;
+        } else if (isDebuggable()) {
+            return msg;
+        }
+
+        return HIDDEN;
+    }
+
+}
diff --git a/tests/unit/src/com/android/telephony/imsmedia/util/LogTest.java b/tests/unit/src/com/android/telephony/imsmedia/util/LogTest.java
new file mode 100644
index 0000000..cd48550
--- /dev/null
+++ b/tests/unit/src/com/android/telephony/imsmedia/util/LogTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 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.telephony.imsmedia.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class LogTest {
+
+    private static final String TAG = "LogTest";
+    private static final String LOG_TEST_MESSAGE = "LogTestMessage";
+
+    @Test
+    @SmallTest
+    public void testInit() {
+        int logLevel = Log.DEFAULT_LOG_LEVEL;
+        Log.init(logLevel);
+        assertEquals(Log.DEFAULT_LOG_LEVEL, Log.getLogLevel());
+
+        logLevel = Log.LOG_LEVEL_DEBUG;
+        Log.init(logLevel);
+        assertEquals(Log.LOG_LEVEL_DEBUG, Log.getLogLevel());
+    }
+
+    @Test
+    @SmallTest
+    public void logLevels() throws Exception {
+
+        Exception throwable = null;
+
+        try {
+            throw new Exception();
+        } catch (Exception e) {
+            throwable = e;
+        }
+
+        // Checks the logs in the logcat.
+        Log.v(TAG, LOG_TEST_MESSAGE + ": v");
+        Log.d(TAG, LOG_TEST_MESSAGE + ": d");
+        Log.i(TAG, LOG_TEST_MESSAGE + ": i");
+        Log.w(TAG, LOG_TEST_MESSAGE + ": w");
+        Log.e(TAG, LOG_TEST_MESSAGE + ": e");
+        Log.e(TAG, LOG_TEST_MESSAGE + ": e with exception", throwable);
+    }
+
+    @Test
+    @SmallTest
+    public void debugConditional() throws Exception {
+        // when debug mode is enabled
+        Log.setDebuggable(true);
+        Log.dc(TAG, LOG_TEST_MESSAGE);
+
+        // when debug mode is disabled
+        Log.setDebuggable(false);
+        Log.dc(TAG, LOG_TEST_MESSAGE + ": debug conditional");
+    }
+
+    @Test
+    @SmallTest
+    public void setDebuggable() throws Exception {
+        Log.setDebuggable(false);
+        assertFalse(Log.isDebuggable());
+
+        Log.setDebuggable(true);
+        assertTrue(Log.isDebuggable());
+    }
+
+    @Test
+    @SmallTest
+    public void setLogLevel() throws Exception {
+        Log.setLogLevel(Log.LOG_LEVEL_INFO);
+        assertFalse(Log.isLogEnabled(Log.LOG_LEVEL_VERBOSE));
+        assertFalse(Log.isLogEnabled(Log.LOG_LEVEL_DEBUG));
+        assertTrue(Log.isLogEnabled(Log.LOG_LEVEL_INFO));
+        assertTrue(Log.isLogEnabled(Log.LOG_LEVEL_WARN));
+        assertTrue(Log.isLogEnabled(Log.LOG_LEVEL_ERROR));
+
+        Log.setLogLevel(Log.DEFAULT_LOG_LEVEL);
+        assertTrue(Log.isLogEnabled(Log.LOG_LEVEL_VERBOSE));
+        assertTrue(Log.isLogEnabled(Log.LOG_LEVEL_DEBUG));
+        assertTrue(Log.isLogEnabled(Log.LOG_LEVEL_INFO));
+        assertTrue(Log.isLogEnabled(Log.LOG_LEVEL_WARN));
+        assertTrue(Log.isLogEnabled(Log.LOG_LEVEL_ERROR));
+    }
+
+    @Test
+    @SmallTest
+    public void hidePii() throws Exception {
+        // when debug mode is enabled
+        Log.setDebuggable(true);
+        String hiddenLogMessage;
+
+        hiddenLogMessage = Log.hidePii(null);
+        assertEquals(Log.NULL, hiddenLogMessage);
+
+        hiddenLogMessage = Log.hidePii("");
+        assertEquals(Log.EMPTY, hiddenLogMessage);
+
+        hiddenLogMessage = Log.hidePii(LOG_TEST_MESSAGE);
+        assertEquals(LOG_TEST_MESSAGE, hiddenLogMessage);
+
+        // when debug mode is disabled
+        Log.setDebuggable(false);
+
+        hiddenLogMessage = Log.hidePii(null);
+        assertEquals(Log.NULL, hiddenLogMessage);
+
+        hiddenLogMessage = Log.hidePii("");
+        assertEquals(Log.EMPTY, hiddenLogMessage);
+
+        hiddenLogMessage = Log.hidePii(LOG_TEST_MESSAGE);
+        assertEquals(Log.HIDDEN, hiddenLogMessage);
+    }
+}