From 91a31a0f352763f0c4fb4d1232a70583466bb68b Mon Sep 17 00:00:00 2001 From: satsukies Date: Fri, 21 Jul 2023 14:02:25 +0900 Subject: [PATCH 01/16] add capture feature implementation --- .../java/com/deploygate/sdk/DeployGate.java | 50 +++++++++++++++++++ .../sdk/ILogcatInstructionSerializer.java | 13 +++++ .../sdk/LogcatInstructionSerializer.java | 26 ++++++++-- .../com/deploygate/sdk/LogcatProcess.java | 20 +++++--- .../com/deploygate/sdk/SendLogcatRequest.java | 30 +++++++++++ .../deploygate/service/DeployGateEvent.java | 5 ++ 6 files changed, 135 insertions(+), 9 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 1282299..c04432c 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -15,13 +15,21 @@ import android.text.TextUtils; import android.util.Log; +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.ProcessLifecycleOwner; + import com.deploygate.sdk.internal.Logger; import com.deploygate.service.DeployGateEvent; import com.deploygate.service.IDeployGateSdkService; import com.deploygate.service.IDeployGateSdkServiceCallback; +import java.util.Date; import java.util.HashSet; import java.util.LinkedList; +import java.util.UUID; import java.util.concurrent.CountDownLatch; /** @@ -74,6 +82,7 @@ public class DeployGate { private String mAppUpdateMessage; private IDeployGateSdkService mRemoteService; + private boolean isCaptureEnabled = false; private final IDeployGateSdkServiceCallback mRemoteCallback = new IDeployGateSdkServiceCallback.Stub() { @@ -110,6 +119,16 @@ public void onEvent( } else { Logger.w("streamed logcat is not supported"); } + } else if (DeployGateEvent.ACTION_DETECT_SCREENSHOT.equals(action)) { + if(isCaptureEnabled) { + Log.d("DeployGate", "isCaptureEnabled is true"); + String uri = extras.getString(DeployGateEvent.EXTRA_SCREENSHOT_URI); + // FIXME: use getString instead of getSerializable + UUID captureId = (UUID) extras.getSerializable(DeployGateEvent.EXTRA_CAPTURE_ID); + requestCreateCapture(uri, captureId); + } else { + Log.d("DeployGate", "isCaptureEnabled is false"); + } } } @@ -182,6 +201,26 @@ private void requestOneshotLogcat() { onOneshotLogcat(); } + private void requestCreateCapture(String uri, UUID captureId) { + if(sInstance == null) { + return; + } + + Date date = new Date(); + date.getTime(); + + Bundle extras = new Bundle(); + extras.putString(DeployGateEvent.EXTRA_SCREENSHOT_URI, uri); + // FIXME: use putString instead of putSerializable + extras.putSerializable(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); + // FIXME: use putString instead of putSerializable + extras.putSerializable(DeployGateEvent.EXTRA_CAPTURE_EVENT_AT, date); + sInstance.invokeAction(DeployGateEvent.ACTION_OPEN_CAPTURE, extras); + + // send logcat for capture + mLogcatInstructionSerializer.requestOneshotLogcat(captureId); + } + private void onOneshotLogcat() { mLogcatInstructionSerializer.requestOneshotLogcat(); } @@ -223,6 +262,17 @@ private DeployGate( CustomLogConfiguration customLogConfiguration, HostApp hostApp ) { + // Response of screenshot detection according to host application lifecycle + ProcessLifecycleOwner.get().getLifecycle().addObserver(new LifecycleEventObserver() { + @Override + public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { + if (event == Lifecycle.Event.ON_START) { + isCaptureEnabled = true; + } else if (event == Lifecycle.Event.ON_STOP) { + isCaptureEnabled = false; + } + } + }); mApplicationContext = applicationContext; mDeployGateClient = new DeployGateClient(applicationContext, DEPLOYGATE_PACKAGE); mHostApp = hostApp; diff --git a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java index c348ba5..2895453 100644 --- a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java @@ -3,6 +3,8 @@ import com.deploygate.sdk.internal.Logger; import com.deploygate.service.IDeployGateSdkService; +import java.util.UUID; + interface ILogcatInstructionSerializer { /** @@ -23,6 +25,11 @@ interface ILogcatInstructionSerializer { */ boolean requestOneshotLogcat(); + /** + * Create and enqueue a request to start sending oneshot logcat with a capture id + */ + boolean requestOneshotLogcat(UUID captureId); + /** * Create and enqueue a request to start sending streamed logcat */ @@ -61,6 +68,12 @@ public boolean requestOneshotLogcat() { return false; } + @Override + public boolean requestOneshotLogcat(UUID captureId) { + Logger.d("Logcat (no-op): requestOneshotLogcat"); + return false; + } + @Override public boolean requestStreamedLogcat(String sessionKey) { Logger.d("Logcat (no-op): requestStreamedLogcat(%s)", sessionKey); diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java index c81ed6e..0a79ee8 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java @@ -11,6 +11,8 @@ import android.text.TextUtils; import android.util.Pair; +import androidx.annotation.Nullable; + import com.deploygate.sdk.internal.Logger; import com.deploygate.service.DeployGateEvent; import com.deploygate.service.IDeployGateSdkService; @@ -19,6 +21,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.Map; +import java.util.UUID; class LogcatInstructionSerializer implements ILogcatInstructionSerializer { static final int MAX_RETRY_COUNT = 2; @@ -73,11 +76,13 @@ public void onStarted(String processId) { @Override public void emit( String processId, - ArrayList logcatLines + ArrayList logcatLines, + @Nullable UUID captureId ) { ensureHandlerPrepared(); - handler.enqueueSendLogcatMessageInstruction(new SendLogcatRequest(processId, logcatLines)); + + handler.enqueueSendLogcatMessageInstruction(new SendLogcatRequest(processId, logcatLines, captureId)); } @Override @@ -111,6 +116,11 @@ public final synchronized boolean requestOneshotLogcat() { return requestLogcat(null); } + @Override + public final synchronized boolean requestOneshotLogcat(UUID captureId) { + return requestLogcat(null, captureId); + } + @Override public boolean requestStreamedLogcat(String sessionKey) { if (TextUtils.isEmpty(sessionKey)) { @@ -183,6 +193,7 @@ int sendSingleChunk( Bundle extras = request.toExtras(); try { + service.sendEvent(packageName, DeployGateEvent.ACTION_SEND_LOGCAT, extras); return SEND_LOGCAT_RESULT_SUCCESS; } catch (RemoteException e) { @@ -216,13 +227,22 @@ int sendSingleChunk( * @return true if new process has lauched */ private boolean requestLogcat(String streamSessionKey) { + return requestLogcat(streamSessionKey, null); + } + + /** + * @param streamSessionKey nullable. sdk can not generate this key. + * @param captureId nullable. + * @return true if new process has lauched + */ + private boolean requestLogcat(String streamSessionKey, @Nullable UUID captureId) { ensureHandlerPrepared(); if (!isEnabled) { return false; } - Pair ids = logcatProcess.execute(streamSessionKey); + Pair ids = logcatProcess.execute(streamSessionKey, captureId); String retiredId = ids.first; String newId = ids.second; diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java index 994c9f9..4b141d7 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java @@ -3,6 +3,8 @@ import android.os.Build; import android.util.Pair; +import androidx.annotation.Nullable; + import com.deploygate.sdk.internal.Logger; import com.deploygate.sdk.internal.annotations.Experimental; @@ -15,6 +17,7 @@ import java.util.Collection; import java.util.Deque; import java.util.List; +import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; @@ -27,7 +30,8 @@ interface Callback { void emit( String processId, - ArrayList logcatLines + ArrayList logcatLines, + @Nullable UUID captureId ); void onFinished(String processId); @@ -58,7 +62,8 @@ void emit( * @return a pair of watcher ids (non-nulls). first is the previous watcher id, second is the new watcher id. */ Pair execute( - @Experimental String streamSessionKey + @Experimental String streamSessionKey, + @Nullable UUID captureId ) { Pair ids; @@ -77,7 +82,7 @@ Pair execute( return Pair.create(currentPid, currentPid); } - final LogcatWatcher newWatcher = new LogcatWatcher(streamSessionKey, callback); + final LogcatWatcher newWatcher = new LogcatWatcher(streamSessionKey, captureId, callback); try { this.latestLogcatWatcher = newWatcher; @@ -121,16 +126,19 @@ static class LogcatWatcher implements Runnable { private final String processId; private final boolean isOneShot; + @Nullable private final UUID captureId; private final WeakReference callback; private final AtomicReference processRef; private final AtomicInteger state; LogcatWatcher( @Experimental String streamSessionKey, + @Nullable UUID captureId, Callback callback ) { this.processId = streamSessionKey != null ? streamSessionKey : ClientId.generate(); this.isOneShot = streamSessionKey == null; + this.captureId = captureId; this.callback = new WeakReference<>(callback); this.processRef = new AtomicReference<>(); this.state = new AtomicInteger(STATE_READY); @@ -228,10 +236,10 @@ public void run() { if (isOneShot) { continue; } else if (logcatBuf.size() >= MAX_LINES) { - callback.emit(processId, toArrayList(logcatBuf)); + callback.emit(processId, toArrayList(logcatBuf), captureId); logcatBuf = createBuffer(MAX_LINES); // Don't reuse to make sure releasing the reference } else if (!bufferedReader.ready()) { - callback.emit(processId, toArrayList(logcatBuf)); + callback.emit(processId, toArrayList(logcatBuf), captureId); logcatBuf = createBuffer(MAX_LINES); // Don't reuse to make sure releasing the reference } else { continue; @@ -248,7 +256,7 @@ public void run() { Callback callback = this.callback.get(); if (callback != null) { - callback.emit(processId, toArrayList(logcatBuf)); + callback.emit(processId, toArrayList(logcatBuf), captureId); } } diff --git a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java index 3050348..d786e14 100644 --- a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java +++ b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java @@ -2,6 +2,8 @@ import android.os.Bundle; +import androidx.annotation.Nullable; + import com.deploygate.sdk.internal.Logger; import com.deploygate.service.DeployGateEvent; @@ -9,6 +11,7 @@ import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.UUID; class SendLogcatRequest extends Instruction { enum Position { @@ -40,6 +43,7 @@ public static SendLogcatRequest createBeginning(String processId) { public final ArrayList lines; public final Position position; + @Nullable public final UUID captureId; private int retryCount; SendLogcatRequest( @@ -49,6 +53,14 @@ public static SendLogcatRequest createBeginning(String processId) { this(pid, lines, Position.Content); } + SendLogcatRequest( + String pid, + List lines, + UUID captureId + ) { + this(pid, lines, Position.Content, captureId); + } + /** * @param pid * a process id. non-null @@ -61,10 +73,26 @@ private SendLogcatRequest( String pid, List lines, Position position + ) { + this(pid, lines, position, null); + } + + /** + * @param pid a process id. non-null + * @param lines logcat contents if available. Zero value is an empty list. + * @param position a position of this request. non-null + * @param captureId the id of the capture. nullable + */ + private SendLogcatRequest( + String pid, + List lines, + Position position, + @Nullable UUID captureId ) { super(pid); this.lines = lines instanceof ArrayList ? (ArrayList) lines : new ArrayList<>(lines); this.position = position; + this.captureId = captureId; } /** @@ -105,5 +133,7 @@ List splitInto(int count) { void applyValues(Bundle extras) { extras.putStringArrayList(DeployGateEvent.EXTRA_LOG, lines); extras.putString(DeployGateEvent.EXTRA_BUNDLE_POSITION, position.label()); + // FIXME: use putString instead of putSerializable + extras.putSerializable(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); } } diff --git a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java index d0d29dd..fe14aed 100644 --- a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java +++ b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java @@ -25,6 +25,8 @@ public interface DeployGateEvent { public static final String ACTION_OPEN_APP_DETAIL = "openAppDetail"; public static final String ACTION_OPEN_COMMENTS = "openComments"; public static final String ACTION_COMPOSE_COMMENT = "composeComment"; + public static final String ACTION_DETECT_SCREENSHOT = "a.detect-screenshot"; + public static final String ACTION_OPEN_CAPTURE = "a.open-capture"; public static final String EXTRA_AUTHOR = "author"; public static final String EXTRA_EXPECTED_AUTHOR = "expectedAuthor"; @@ -85,4 +87,7 @@ public interface DeployGateEvent { public static final String EXTRA_DEPLOYGATE_VERSION_CODE = "deploygateVersionCode"; public static final String EXTRA_COMMENT = "comment"; public static final String EXTRA_DISTRIBUTION_USER_NAME = "distributionUserName"; + public static final String EXTRA_SCREENSHOT_URI = "e.screenshot-uri"; + public static final String EXTRA_CAPTURE_ID = "e.capture-id"; + public static final String EXTRA_CAPTURE_EVENT_AT = "e.capture-event-at"; } From 2f2ebef198338441a40524bfdee3a86ee32d73c9 Mon Sep 17 00:00:00 2001 From: satsukies Date: Fri, 21 Jul 2023 15:23:22 +0900 Subject: [PATCH 02/16] use System.currentTimeMillis() instead of java.util.Date --- sdk/src/main/java/com/deploygate/sdk/DeployGate.java | 8 +++----- .../main/java/com/deploygate/service/DeployGateEvent.java | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index c04432c..f4ceb74 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -26,7 +26,6 @@ import com.deploygate.service.IDeployGateSdkService; import com.deploygate.service.IDeployGateSdkServiceCallback; -import java.util.Date; import java.util.HashSet; import java.util.LinkedList; import java.util.UUID; @@ -206,15 +205,14 @@ private void requestCreateCapture(String uri, UUID captureId) { return; } - Date date = new Date(); - date.getTime(); + // capture timestamp + long eventAtMillis = System.currentTimeMillis(); Bundle extras = new Bundle(); extras.putString(DeployGateEvent.EXTRA_SCREENSHOT_URI, uri); // FIXME: use putString instead of putSerializable extras.putSerializable(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); - // FIXME: use putString instead of putSerializable - extras.putSerializable(DeployGateEvent.EXTRA_CAPTURE_EVENT_AT, date); + extras.putLong(DeployGateEvent.EXTRA_CAPTURE_EVENT_AT, eventAtMillis); sInstance.invokeAction(DeployGateEvent.ACTION_OPEN_CAPTURE, extras); // send logcat for capture diff --git a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java index fe14aed..9abc595 100644 --- a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java +++ b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java @@ -89,5 +89,10 @@ public interface DeployGateEvent { public static final String EXTRA_DISTRIBUTION_USER_NAME = "distributionUserName"; public static final String EXTRA_SCREENSHOT_URI = "e.screenshot-uri"; public static final String EXTRA_CAPTURE_ID = "e.capture-id"; + + /** + * the timestamp of the capture. + * this value must be compatible EpochMillis. + */ public static final String EXTRA_CAPTURE_EVENT_AT = "e.capture-event-at"; } From aa3d147d3f677a618e42829bb291cc4cf14b43f0 Mon Sep 17 00:00:00 2001 From: satsukies Date: Fri, 21 Jul 2023 15:33:47 +0900 Subject: [PATCH 03/16] use stringified value instead of UUID instance --- .../java/com/deploygate/sdk/DeployGate.java | 9 +++------ .../sdk/ILogcatInstructionSerializer.java | 6 ++---- .../sdk/LogcatInstructionSerializer.java | 7 +++---- .../java/com/deploygate/sdk/LogcatProcess.java | 17 ++++++++--------- .../com/deploygate/sdk/SendLogcatRequest.java | 10 ++++------ .../com/deploygate/service/DeployGateEvent.java | 5 +++++ 6 files changed, 25 insertions(+), 29 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index f4ceb74..256ecb0 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -28,7 +28,6 @@ import java.util.HashSet; import java.util.LinkedList; -import java.util.UUID; import java.util.concurrent.CountDownLatch; /** @@ -122,8 +121,7 @@ public void onEvent( if(isCaptureEnabled) { Log.d("DeployGate", "isCaptureEnabled is true"); String uri = extras.getString(DeployGateEvent.EXTRA_SCREENSHOT_URI); - // FIXME: use getString instead of getSerializable - UUID captureId = (UUID) extras.getSerializable(DeployGateEvent.EXTRA_CAPTURE_ID); + String captureId = extras.getString(DeployGateEvent.EXTRA_CAPTURE_ID); requestCreateCapture(uri, captureId); } else { Log.d("DeployGate", "isCaptureEnabled is false"); @@ -200,7 +198,7 @@ private void requestOneshotLogcat() { onOneshotLogcat(); } - private void requestCreateCapture(String uri, UUID captureId) { + private void requestCreateCapture(String uri, String captureId) { if(sInstance == null) { return; } @@ -210,8 +208,7 @@ private void requestCreateCapture(String uri, UUID captureId) { Bundle extras = new Bundle(); extras.putString(DeployGateEvent.EXTRA_SCREENSHOT_URI, uri); - // FIXME: use putString instead of putSerializable - extras.putSerializable(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); + extras.putString(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); extras.putLong(DeployGateEvent.EXTRA_CAPTURE_EVENT_AT, eventAtMillis); sInstance.invokeAction(DeployGateEvent.ACTION_OPEN_CAPTURE, extras); diff --git a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java index 2895453..5bb7c74 100644 --- a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java @@ -3,8 +3,6 @@ import com.deploygate.sdk.internal.Logger; import com.deploygate.service.IDeployGateSdkService; -import java.util.UUID; - interface ILogcatInstructionSerializer { /** @@ -28,7 +26,7 @@ interface ILogcatInstructionSerializer { /** * Create and enqueue a request to start sending oneshot logcat with a capture id */ - boolean requestOneshotLogcat(UUID captureId); + boolean requestOneshotLogcat(String captureId); /** * Create and enqueue a request to start sending streamed logcat @@ -69,7 +67,7 @@ public boolean requestOneshotLogcat() { } @Override - public boolean requestOneshotLogcat(UUID captureId) { + public boolean requestOneshotLogcat(String captureId) { Logger.d("Logcat (no-op): requestOneshotLogcat"); return false; } diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java index 0a79ee8..2cf718d 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java @@ -21,7 +21,6 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.Map; -import java.util.UUID; class LogcatInstructionSerializer implements ILogcatInstructionSerializer { static final int MAX_RETRY_COUNT = 2; @@ -77,7 +76,7 @@ public void onStarted(String processId) { public void emit( String processId, ArrayList logcatLines, - @Nullable UUID captureId + @Nullable String captureId ) { ensureHandlerPrepared(); @@ -117,7 +116,7 @@ public final synchronized boolean requestOneshotLogcat() { } @Override - public final synchronized boolean requestOneshotLogcat(UUID captureId) { + public final synchronized boolean requestOneshotLogcat(String captureId) { return requestLogcat(null, captureId); } @@ -235,7 +234,7 @@ private boolean requestLogcat(String streamSessionKey) { * @param captureId nullable. * @return true if new process has lauched */ - private boolean requestLogcat(String streamSessionKey, @Nullable UUID captureId) { + private boolean requestLogcat(String streamSessionKey, @Nullable String captureId) { ensureHandlerPrepared(); if (!isEnabled) { diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java index 4b141d7..f6a4943 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java @@ -17,7 +17,6 @@ import java.util.Collection; import java.util.Deque; import java.util.List; -import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; @@ -29,9 +28,9 @@ interface Callback { void onStarted(String processId); void emit( - String processId, - ArrayList logcatLines, - @Nullable UUID captureId + String processId, + ArrayList logcatLines, + @Nullable String captureId ); void onFinished(String processId); @@ -63,7 +62,7 @@ void emit( */ Pair execute( @Experimental String streamSessionKey, - @Nullable UUID captureId + @Nullable String captureId ) { Pair ids; @@ -126,15 +125,15 @@ static class LogcatWatcher implements Runnable { private final String processId; private final boolean isOneShot; - @Nullable private final UUID captureId; + @Nullable private final String captureId; private final WeakReference callback; private final AtomicReference processRef; private final AtomicInteger state; LogcatWatcher( - @Experimental String streamSessionKey, - @Nullable UUID captureId, - Callback callback + @Experimental String streamSessionKey, + @Nullable String captureId, + Callback callback ) { this.processId = streamSessionKey != null ? streamSessionKey : ClientId.generate(); this.isOneShot = streamSessionKey == null; diff --git a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java index d786e14..92ad634 100644 --- a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java +++ b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java @@ -11,7 +11,6 @@ import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.UUID; class SendLogcatRequest extends Instruction { enum Position { @@ -43,7 +42,7 @@ public static SendLogcatRequest createBeginning(String processId) { public final ArrayList lines; public final Position position; - @Nullable public final UUID captureId; + @Nullable public final String captureId; private int retryCount; SendLogcatRequest( @@ -56,7 +55,7 @@ public static SendLogcatRequest createBeginning(String processId) { SendLogcatRequest( String pid, List lines, - UUID captureId + String captureId ) { this(pid, lines, Position.Content, captureId); } @@ -87,7 +86,7 @@ private SendLogcatRequest( String pid, List lines, Position position, - @Nullable UUID captureId + @Nullable String captureId ) { super(pid); this.lines = lines instanceof ArrayList ? (ArrayList) lines : new ArrayList<>(lines); @@ -133,7 +132,6 @@ List splitInto(int count) { void applyValues(Bundle extras) { extras.putStringArrayList(DeployGateEvent.EXTRA_LOG, lines); extras.putString(DeployGateEvent.EXTRA_BUNDLE_POSITION, position.label()); - // FIXME: use putString instead of putSerializable - extras.putSerializable(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); + extras.putString(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); } } diff --git a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java index 9abc595..7011bc8 100644 --- a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java +++ b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java @@ -88,6 +88,11 @@ public interface DeployGateEvent { public static final String EXTRA_COMMENT = "comment"; public static final String EXTRA_DISTRIBUTION_USER_NAME = "distributionUserName"; public static final String EXTRA_SCREENSHOT_URI = "e.screenshot-uri"; + + /** + * the id of the capture. + * this value must be generated by UUIDv4. + */ public static final String EXTRA_CAPTURE_ID = "e.capture-id"; /** From 369b2675cd39bdbb7aece3e9fc664988b1a43c6e Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 15 Aug 2023 11:02:43 +0900 Subject: [PATCH 04/16] fix: Send real time elapsed on app goes foreground instead of stateful sdk implementation --- sdk.build.gradle | 2 + .../java/com/deploygate/sdk/DeployGate.java | 79 +++++++------------ .../sdk/ILogcatInstructionSerializer.java | 28 +++---- .../sdk/LogcatInstructionSerializer.java | 59 ++++++++------ .../VisibilityLifecycleCallbacks.java | 74 +++++++++++++++++ .../deploygate/service/DeployGateEvent.java | 11 +-- .../sdk/LogcatInstructionSerializerTest.java | 65 ++++++++++++--- 7 files changed, 208 insertions(+), 110 deletions(-) create mode 100644 sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java diff --git a/sdk.build.gradle b/sdk.build.gradle index 6dab261..f2001a8 100644 --- a/sdk.build.gradle +++ b/sdk.build.gradle @@ -40,6 +40,8 @@ android { } dependencies { + implementation "androidx.annotation:annotation:1.6.0" + testImplementation 'androidx.test:runner:1.5.2' testImplementation 'androidx.test.ext:junit:1.1.5' testImplementation 'org.robolectric:robolectric:4.10.3' diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 256ecb0..5fcbc14 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -16,11 +16,9 @@ import android.util.Log; import androidx.annotation.NonNull; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleEventObserver; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.ProcessLifecycleOwner; +import androidx.annotation.Nullable; +import com.deploygate.sdk.internal.VisibilityLifecycleCallbacks; import com.deploygate.sdk.internal.Logger; import com.deploygate.service.DeployGateEvent; import com.deploygate.service.IDeployGateSdkService; @@ -29,6 +27,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * This is DeployGate SDK library. Import this library to the application @@ -80,13 +79,12 @@ public class DeployGate { private String mAppUpdateMessage; private IDeployGateSdkService mRemoteService; - private boolean isCaptureEnabled = false; private final IDeployGateSdkServiceCallback mRemoteCallback = new IDeployGateSdkServiceCallback.Stub() { public void onEvent( - String action, - Bundle extras + @NonNull String action, + @NonNull Bundle extras ) throws RemoteException { if (TextUtils.isEmpty(action)) { return; @@ -97,7 +95,8 @@ public void onEvent( } else if (DeployGateEvent.ACTION_UPDATE_AVAILABLE.equals(action)) { onUpdateArrived(extras.getInt(DeployGateEvent.EXTRA_SERIAL), extras.getString(DeployGateEvent.EXTRA_VERSION_NAME), extras.getInt(DeployGateEvent.EXTRA_VERSION_CODE), extras.getString(DeployGateEvent.EXTRA_SERIAL_MESSAGE)); } else if (DeployGateEvent.ACTION_ONESHOT_LOGCAT.equals(action)) { - onOneshotLogcat(); + String captureId = extras.getString(DeployGateEvent.EXTRA_CAPTURE_ID); + onOneshotLogcat(captureId); } else if (DeployGateEvent.ACTION_ENABLE_LOGCAT.equals(action)) { if (mDeployGateClient.isSupported(Compatibility.STREAMED_LOGCAT)) { String sessionKey = extras.getString(DeployGateEvent.EXTRA_LOGCAT_STREAM_SESSION_KEY); @@ -117,15 +116,8 @@ public void onEvent( } else { Logger.w("streamed logcat is not supported"); } - } else if (DeployGateEvent.ACTION_DETECT_SCREENSHOT.equals(action)) { - if(isCaptureEnabled) { - Log.d("DeployGate", "isCaptureEnabled is true"); - String uri = extras.getString(DeployGateEvent.EXTRA_SCREENSHOT_URI); - String captureId = extras.getString(DeployGateEvent.EXTRA_CAPTURE_ID); - requestCreateCapture(uri, captureId); - } else { - Log.d("DeployGate", "isCaptureEnabled is false"); - } + } else { + Logger.w("%s is not supported by this sdk version", action); } } @@ -194,30 +186,26 @@ public void run() { } }; - private void requestOneshotLogcat() { - onOneshotLogcat(); - } - - private void requestCreateCapture(String uri, String captureId) { - if(sInstance == null) { - return; + @SuppressWarnings("FieldCanBeLocal") + @NonNull + private final VisibilityLifecycleCallbacks.OnVisibilityChangeListener mOnVisibilityChangeListener = new VisibilityLifecycleCallbacks.OnVisibilityChangeListener() { + @Override + public void onForeground( + long elapsedRealtime, + @NonNull TimeUnit timeUnit + ) { + Bundle extras = new Bundle(); + extras.putLong(DeployGateEvent.EXTRA_FOREGROUND_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); + invokeAction(DeployGateEvent.ACTION_GO_TO_FOREGROUND, extras); } + }; - // capture timestamp - long eventAtMillis = System.currentTimeMillis(); - - Bundle extras = new Bundle(); - extras.putString(DeployGateEvent.EXTRA_SCREENSHOT_URI, uri); - extras.putString(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); - extras.putLong(DeployGateEvent.EXTRA_CAPTURE_EVENT_AT, eventAtMillis); - sInstance.invokeAction(DeployGateEvent.ACTION_OPEN_CAPTURE, extras); - - // send logcat for capture - mLogcatInstructionSerializer.requestOneshotLogcat(captureId); + private void requestOneshotLogcat() { + onOneshotLogcat(null); } - private void onOneshotLogcat() { - mLogcatInstructionSerializer.requestOneshotLogcat(); + private void onOneshotLogcat(@Nullable String captureId) { + mLogcatInstructionSerializer.requestOneshotLogcat(captureId); } private void onEnableStreamedLogcat(String streamSessionKey) { @@ -257,18 +245,8 @@ private DeployGate( CustomLogConfiguration customLogConfiguration, HostApp hostApp ) { - // Response of screenshot detection according to host application lifecycle - ProcessLifecycleOwner.get().getLifecycle().addObserver(new LifecycleEventObserver() { - @Override - public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { - if (event == Lifecycle.Event.ON_START) { - isCaptureEnabled = true; - } else if (event == Lifecycle.Event.ON_STOP) { - isCaptureEnabled = false; - } - } - }); mApplicationContext = applicationContext; + VisibilityLifecycleCallbacks mVisibilityLifecycleCallbacks = new VisibilityLifecycleCallbacks(mOnVisibilityChangeListener); mDeployGateClient = new DeployGateClient(applicationContext, DEPLOYGATE_PACKAGE); mHostApp = hostApp; mHandler = new Handler(); @@ -284,6 +262,7 @@ public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Ev } mInitializedLatch = new CountDownLatch(1); + ((Application) applicationContext).registerActivityLifecycleCallbacks(mVisibilityLifecycleCallbacks); initService(true); } @@ -364,8 +343,8 @@ private void requestServiceInit(final boolean isBoot) { } private void invokeAction( - String action, - Bundle extras + @NonNull String action, + @Nullable Bundle extras ) { if (mRemoteService == null) { return; diff --git a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java index 5bb7c74..973d0fb 100644 --- a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java @@ -1,5 +1,8 @@ package com.deploygate.sdk; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.deploygate.sdk.internal.Logger; import com.deploygate.service.IDeployGateSdkService; @@ -11,7 +14,7 @@ interface ILogcatInstructionSerializer { * @param service * the latest service connection */ - void connect(IDeployGateSdkService service); + void connect(@NonNull IDeployGateSdkService service); /** * Release a service connection and cancel all pending instructions and on-going instruction. @@ -20,19 +23,16 @@ interface ILogcatInstructionSerializer { /** * Create and enqueue a request to start sending oneshot logcat + * + * @param captureId this is nullable. Set to non-null if this logcat is for a capture. */ - boolean requestOneshotLogcat(); - - /** - * Create and enqueue a request to start sending oneshot logcat with a capture id - */ - boolean requestOneshotLogcat(String captureId); + boolean requestOneshotLogcat(@Nullable String captureId); /** * Create and enqueue a request to start sending streamed logcat */ boolean requestStreamedLogcat( - String sessionKey + @Nullable String sessionKey ); /** @@ -51,7 +51,7 @@ boolean requestStreamedLogcat( ILogcatInstructionSerializer NULL_INSTANCE = new ILogcatInstructionSerializer() { @Override - public void connect(IDeployGateSdkService service) { + public void connect(@NonNull IDeployGateSdkService service) { Logger.d("Logcat (no-op): connect"); } @@ -60,20 +60,14 @@ public void disconnect() { Logger.d("Logcat (no-op): disconnect"); } - @Override - public boolean requestOneshotLogcat() { - Logger.d("Logcat (no-op): requestOneshotLogcat"); - return false; - } - @Override public boolean requestOneshotLogcat(String captureId) { - Logger.d("Logcat (no-op): requestOneshotLogcat"); + Logger.d("Logcat (no-op): requestOneshotLogcat(%s)", captureId != null ? captureId : "null"); return false; } @Override - public boolean requestStreamedLogcat(String sessionKey) { + public boolean requestStreamedLogcat(@Nullable String sessionKey) { Logger.d("Logcat (no-op): requestStreamedLogcat(%s)", sessionKey); return false; } diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java index 2cf718d..bc7e591 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java @@ -11,6 +11,7 @@ import android.text.TextUtils; import android.util.Pair; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.deploygate.sdk.internal.Logger; @@ -33,31 +34,37 @@ class LogcatInstructionSerializer implements ILogcatInstructionSerializer { static final int WHAT_SEND_LOGCAT = 0x20; static final int WHAT_ADD_LOGCAT_CHUNK = 0x30; + @NonNull private static final Object LOCK = new Object(); + @NonNull private final String packageName; /** * nullable if logcat is not supported on this device */ + @Nullable private final LogcatProcess logcatProcess; /** * nullable if logcat is not supported on this device */ @SuppressWarnings("FieldCanBeLocal") + @NonNull private final HandlerThread thread; /** * NonNull if prepared once, however this is always null Logcat is not supported */ + @Nullable private LogcatHandler handler; private boolean isEnabled; + @Nullable private volatile IDeployGateSdkService service; LogcatInstructionSerializer( - String packageName + @NonNull String packageName ) { if (TextUtils.isEmpty(packageName)) { throw new IllegalArgumentException("packageName must be present"); @@ -68,24 +75,26 @@ class LogcatInstructionSerializer implements ILogcatInstructionSerializer { this.logcatProcess = new LogcatProcess(new LogcatProcess.Callback() { @Override - public void onStarted(String processId) { + public void onStarted(@NonNull String processId) { + //noinspection ConstantConditions handler.enqueueSendLogcatMessageInstruction(SendLogcatRequest.createBeginning(processId)); } @Override public void emit( - String processId, - ArrayList logcatLines, - @Nullable String captureId + @NonNull String processId, + @NonNull ArrayList logcatLines, + @Nullable String captureId ) { ensureHandlerPrepared(); - + //noinspection ConstantConditions handler.enqueueSendLogcatMessageInstruction(new SendLogcatRequest(processId, logcatLines, captureId)); } @Override - public void onFinished(String processId) { + public void onFinished(@NonNull String processId) { + //noinspection ConstantConditions handler.enqueueSendLogcatMessageInstruction(SendLogcatRequest.createTermination(processId)); } }); @@ -94,7 +103,8 @@ public void onFinished(String processId) { } @Override - public final synchronized void connect(IDeployGateSdkService service) { + public final synchronized void connect(@NonNull IDeployGateSdkService service) { + //noinspection ConstantConditions if (service == null) { throw new IllegalArgumentException("service must not be null"); } @@ -111,17 +121,12 @@ public final void disconnect() { } @Override - public final synchronized boolean requestOneshotLogcat() { - return requestLogcat(null); - } - - @Override - public final synchronized boolean requestOneshotLogcat(String captureId) { + public final synchronized boolean requestOneshotLogcat(@Nullable String captureId) { return requestLogcat(null, captureId); } @Override - public boolean requestStreamedLogcat(String sessionKey) { + public boolean requestStreamedLogcat(@Nullable String sessionKey) { if (TextUtils.isEmpty(sessionKey)) { Logger.w("non-blank stream key is required"); return false; @@ -145,7 +150,10 @@ public final void setEnabled(boolean enabled) { public final void stopStream() { ensureHandlerPrepared(); - logcatProcess.stop(); + if (logcatProcess != null) { + logcatProcess.stop(); + } + //noinspection ConstantConditions handler.cancelPendingSendLogcatInstruction(); } @@ -174,7 +182,7 @@ public final boolean hasServiceConnection() { * otherwise {@link LogcatInstructionSerializer#SEND_LOGCAT_RESULT_FAILURE_RETRY_EXCEEDED} */ int sendSingleChunk( - SendLogcatRequest request + @NonNull SendLogcatRequest request ) { IDeployGateSdkService service = this.service; @@ -225,7 +233,7 @@ int sendSingleChunk( * * @return true if new process has lauched */ - private boolean requestLogcat(String streamSessionKey) { + private boolean requestLogcat(@Nullable String streamSessionKey) { return requestLogcat(streamSessionKey, null); } @@ -234,10 +242,10 @@ private boolean requestLogcat(String streamSessionKey) { * @param captureId nullable. * @return true if new process has lauched */ - private boolean requestLogcat(String streamSessionKey, @Nullable String captureId) { + private boolean requestLogcat(@Nullable String streamSessionKey, @Nullable String captureId) { ensureHandlerPrepared(); - if (!isEnabled) { + if (!isEnabled || logcatProcess == null) { return false; } @@ -253,6 +261,7 @@ private boolean requestLogcat(String streamSessionKey, @Nullable String captureI if (!LogcatProcess.UNKNOWN_PROCESS_ID.equals(retiredId)) { // the previous on-going execution has been retied + //noinspection ConstantConditions handler.cancelPendingSendLogcatInstruction(retiredId); } @@ -280,7 +289,7 @@ private void ensureHandlerPrepared() { */ private int sendChunkedLogcats( int splitTimes, - LinkedList pendingRequests + @NonNull LinkedList pendingRequests ) { SendLogcatRequest request = pendingRequests.removeFirst(); @@ -306,10 +315,12 @@ private int sendChunkedLogcats( */ /** - * @return the handler instance or null if not prepared. + * @return the handler instance */ + @NonNull Handler getHandler() { ensureHandlerPrepared(); + //noinspection ConstantConditions return handler; } @@ -346,8 +357,6 @@ private static class LogcatHandler extends Handler { /** * Cancel the send-logcat instruction of all watchers in the handler message queue. - * - * @return true if canceled, otherwise false. */ void cancelPendingSendLogcatInstruction() { synchronized (requestMap) { @@ -360,8 +369,6 @@ void cancelPendingSendLogcatInstruction() { /** * Cancel the send-logcat instruction of the specific watcher in the handler message queue. - * - * @return true if canceled, otherwise false. */ void cancelPendingSendLogcatInstruction(String bundleId) { acquireRequests(bundleId); diff --git a/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java b/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java new file mode 100644 index 0000000..2787b1e --- /dev/null +++ b/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java @@ -0,0 +1,74 @@ +package com.deploygate.sdk.internal; + +import android.app.Activity; +import android.app.Application; +import android.os.Build; +import android.os.Bundle; +import android.os.SystemClock; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.concurrent.TimeUnit; + +public final class VisibilityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { + public interface OnVisibilityChangeListener { + void onForeground(long elapsedRealtime, @NonNull TimeUnit timeUnit); + } + + private int onResumeCount = 0; // this is manipulated from the single thread + + @NonNull + private final OnVisibilityChangeListener listener; + + public VisibilityLifecycleCallbacks(@NonNull OnVisibilityChangeListener listener) { + this.listener = listener; + } + + @Override + public void onActivityCreated( + @NonNull Activity activity, + @Nullable Bundle savedInstanceState + ) { + // no-op + } + + @Override + public void onActivityStarted(@NonNull Activity activity) { + // no-op + } + + @Override + public void onActivityResumed(@NonNull Activity activity) { + onResumeCount++; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + listener.onForeground(SystemClock.elapsedRealtimeNanos(), TimeUnit.NANOSECONDS); + } else { + listener.onForeground(SystemClock.elapsedRealtime(), TimeUnit.MILLISECONDS); + } + } + + @Override + public void onActivityPaused(@NonNull Activity activity) { + onResumeCount = Math.max(onResumeCount - 1, 0); // cuz uint is unavailable. + } + + @Override + public void onActivityStopped(@NonNull Activity activity) { + // no-op + } + + @Override + public void onActivitySaveInstanceState( + @NonNull Activity activity, + @NonNull Bundle outState + ) { + // no-op + } + + @Override + public void onActivityDestroyed(@NonNull Activity activity) { + // no-op + } +} diff --git a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java index 7011bc8..b99f1b3 100644 --- a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java +++ b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java @@ -25,8 +25,7 @@ public interface DeployGateEvent { public static final String ACTION_OPEN_APP_DETAIL = "openAppDetail"; public static final String ACTION_OPEN_COMMENTS = "openComments"; public static final String ACTION_COMPOSE_COMMENT = "composeComment"; - public static final String ACTION_DETECT_SCREENSHOT = "a.detect-screenshot"; - public static final String ACTION_OPEN_CAPTURE = "a.open-capture"; + public static final String ACTION_GO_TO_FOREGROUND = "a.go-to-foreground"; public static final String EXTRA_AUTHOR = "author"; public static final String EXTRA_EXPECTED_AUTHOR = "expectedAuthor"; @@ -87,17 +86,15 @@ public interface DeployGateEvent { public static final String EXTRA_DEPLOYGATE_VERSION_CODE = "deploygateVersionCode"; public static final String EXTRA_COMMENT = "comment"; public static final String EXTRA_DISTRIBUTION_USER_NAME = "distributionUserName"; - public static final String EXTRA_SCREENSHOT_URI = "e.screenshot-uri"; /** * the id of the capture. - * this value must be generated by UUIDv4. */ public static final String EXTRA_CAPTURE_ID = "e.capture-id"; /** - * the timestamp of the capture. - * this value must be compatible EpochMillis. + * The elapsed real time since boot at the time when the app goes to foreground. + * this value must be nano times. */ - public static final String EXTRA_CAPTURE_EVENT_AT = "e.capture-event-at"; + public static final String EXTRA_FOREGROUND_EVENT_ELAPSED_REAL_TIME_IN_NANOS = "e.foreground-event-elapsed-real-time"; } diff --git a/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java b/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java index 1d6667d..8b2dc62 100644 --- a/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java @@ -102,7 +102,26 @@ public void after() { public void sendSingleChunk_always_returns_retriable_status_if_service_is_none() throws RemoteException { instructionSerializer = new LogcatInstructionSerializer(PACKAGE_NAME); - instructionSerializer.requestOneshotLogcat(); + instructionSerializer.requestOneshotLogcat(null); + + SendLogcatRequest chunk1 = new SendLogcatRequest("tid1", new ArrayList<>(Arrays.asList("line1", "line2", "line3"))); + SendLogcatRequest chunk2 = new SendLogcatRequest("tid2", new ArrayList<>(Arrays.asList("line4", "line5", "line6"))); + SendLogcatRequest chunk3 = new SendLogcatRequest("tid3", new ArrayList<>(Arrays.asList("line7", "line8", "line9"))); + + doNothing().when(service).sendEvent(anyString(), anyString(), any(Bundle.class)); + + Truth.assertThat(instructionSerializer.sendSingleChunk(chunk1)).isEqualTo(LogcatInstructionSerializer.SEND_LOGCAT_RESULT_FAILURE_RETRIABLE); + Truth.assertThat(instructionSerializer.sendSingleChunk(chunk2)).isEqualTo(LogcatInstructionSerializer.SEND_LOGCAT_RESULT_FAILURE_RETRIABLE); + Truth.assertThat(instructionSerializer.sendSingleChunk(chunk3)).isEqualTo(LogcatInstructionSerializer.SEND_LOGCAT_RESULT_FAILURE_RETRIABLE); + + Mockito.verifyNoInteractions(service); + } + + @Test(timeout = 3000L) + public void sendSingleChunk_always_returns_retriable_status_if_service_is_none_and_is_in_capture_mode() throws RemoteException { + instructionSerializer = new LogcatInstructionSerializer(PACKAGE_NAME); + + instructionSerializer.requestOneshotLogcat("brabra"); SendLogcatRequest chunk1 = new SendLogcatRequest("tid1", new ArrayList<>(Arrays.asList("line1", "line2", "line3"))); SendLogcatRequest chunk2 = new SendLogcatRequest("tid2", new ArrayList<>(Arrays.asList("line4", "line5", "line6"))); @@ -188,7 +207,15 @@ public void requestSendingLogcat_works_regardless_of_service() throws RemoteExce // don't fail for (int i = 0; i < 10; i++) { - instructionSerializer.requestOneshotLogcat(); + instructionSerializer.requestOneshotLogcat(null); + } + + Shadows.shadowOf(instructionSerializer.getHandler().getLooper()).idle(); + + // don't fail + + for (int i = 0; i < 10; i++) { + instructionSerializer.requestOneshotLogcat("brabra"); } Shadows.shadowOf(instructionSerializer.getHandler().getLooper()).idle(); @@ -203,10 +230,19 @@ public void requestSendingLogcat_does_nothing_if_disabled() throws RemoteExcepti instructionSerializer.setEnabled(false); for (int i = 0; i < 30; i++) { - if (i % 2 == 0) { - Truth.assertThat(instructionSerializer.requestOneshotLogcat()).isFalse(); - } else { - Truth.assertThat(instructionSerializer.requestStreamedLogcat("bsk")).isFalse(); + switch (i % 3) { + case 0: { + Truth.assertThat(instructionSerializer.requestOneshotLogcat(null)).isFalse(); + break; + } + case 1: { + Truth.assertThat(instructionSerializer.requestStreamedLogcat("bsk")).isFalse(); + break; + } + case 2: { + Truth.assertThat(instructionSerializer.requestOneshotLogcat("brabra")).isFalse(); + break; + } } } @@ -214,10 +250,19 @@ public void requestSendingLogcat_does_nothing_if_disabled() throws RemoteExcepti instructionSerializer.connect(service); for (int i = 0; i < 30; i++) { - if (i % 2 == 0) { - Truth.assertThat(instructionSerializer.requestOneshotLogcat()).isFalse(); - } else { - Truth.assertThat(instructionSerializer.requestStreamedLogcat("bsk")).isFalse(); + switch (i % 3) { + case 0: { + Truth.assertThat(instructionSerializer.requestOneshotLogcat(null)).isFalse(); + break; + } + case 1: { + Truth.assertThat(instructionSerializer.requestStreamedLogcat("bsk")).isFalse(); + break; + } + case 2: { + Truth.assertThat(instructionSerializer.requestOneshotLogcat("brabra")).isFalse(); + break; + } } } From d2203d520a822fcacc80f9f6ae2e4382137b7741 Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 15 Aug 2023 11:25:09 +0900 Subject: [PATCH 05/16] chore: Add jitpack configuration to specify jdk version --- jitpack.yml | 5 +++ .../com/deploygate/sdk/LogcatProcess.java | 17 +++++---- .../com/deploygate/sdk/LogcatProcessTest.java | 35 ++++++++++--------- 3 files changed, 35 insertions(+), 22 deletions(-) create mode 100644 jitpack.yml diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000..2837436 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,5 @@ +jdk: + - openjdk17 +before_install: + - sdk install java 17.0.8-tem + - sdk use java 17.0.8-tem diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java index f6a4943..c28fa61 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java @@ -3,6 +3,7 @@ import android.os.Build; import android.util.Pair; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.deploygate.sdk.internal.Logger; @@ -123,18 +124,22 @@ static class LogcatWatcher implements Runnable { private static final int STATE_INTERRUPTED = 2; private static final int STATE_FINISHED = 3; - private final String processId; + @NonNull private final String processId; private final boolean isOneShot; @Nullable private final String captureId; - private final WeakReference callback; - private final AtomicReference processRef; - private final AtomicInteger state; + @NonNull private final WeakReference callback; + @NonNull private final AtomicReference processRef; + @NonNull private final AtomicInteger state; LogcatWatcher( - @Experimental String streamSessionKey, + @Experimental @Nullable String streamSessionKey, @Nullable String captureId, - Callback callback + @NonNull Callback callback ) { + if (streamSessionKey != null && captureId != null) { + throw new IllegalArgumentException("either of stream session key and capture id can be present"); + } + this.processId = streamSessionKey != null ? streamSessionKey : ClientId.generate(); this.isOneShot = streamSessionKey == null; this.captureId = captureId; diff --git a/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java b/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java index 4a0dd0d..0a29ebb 100644 --- a/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java @@ -1,5 +1,7 @@ package com.deploygate.sdk; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.deploygate.sdk.helper.FakeLogcat; @@ -72,7 +74,7 @@ public void nonOneShot_emits_multiple_log_chunks() { try { fakeLogcat = new FakeLogcat(10); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bsk1", capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bsk1", null, capture); watcher.run(); @@ -89,7 +91,7 @@ public void nonOneShot_emits_multiple_log_chunks() { try { fakeLogcat = new FakeLogcat(501); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks2", capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks2", null, capture); watcher.run(); @@ -110,7 +112,7 @@ public void nonOneShot_emits_multiple_log_chunks() { try { fakeLogcat = new FakeLogcat(1000); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks3", capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks3", null, capture); watcher.run(); @@ -136,7 +138,7 @@ public void OneShot_emits_single_log_chunk() { try { fakeLogcat = new FakeLogcat(10); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, null, capture); watcher.run(); @@ -153,7 +155,7 @@ public void OneShot_emits_single_log_chunk() { try { fakeLogcat = new FakeLogcat(501); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, "cap2", capture); watcher.run(); @@ -173,7 +175,7 @@ public void OneShot_emits_single_log_chunk() { try { fakeLogcat = new FakeLogcat(1000); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, null, capture); watcher.run(); @@ -200,7 +202,7 @@ public void nonOneShot_interrupt_stops_later_emits() { // call the method for the finished process fakeLogcat = new FakeLogcat(10); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks1", capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks1", null, capture); watcher.run(); @@ -221,7 +223,7 @@ public void nonOneShot_interrupt_stops_later_emits() { // interrupt the on-going process that read lines less than MAX_LINES fakeLogcat = new FakeLogcat(20, 10); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks2", capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks2", "cap2", capture); destroyWorkerAfter(watcher, 300, TimeUnit.MILLISECONDS); @@ -240,7 +242,7 @@ public void nonOneShot_interrupt_stops_later_emits() { // interrupt the on-going process that read many lines more than MAX_LINES fakeLogcat = new FakeLogcat(550, 549); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks3", capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks3", null, capture); destroyWorkerAfter(watcher, 500, TimeUnit.MILLISECONDS); @@ -269,7 +271,7 @@ public void OneShot_interrupt_stops_emitting_logs() { // call the method for the finished process fakeLogcat = new FakeLogcat(10); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, null, capture); watcher.run(); @@ -290,7 +292,7 @@ public void OneShot_interrupt_stops_emitting_logs() { // interrupt the on-going process that read lines less than MAX_LINES fakeLogcat = new FakeLogcat(20, 10); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, "cap2", capture); destroyWorkerAfter(watcher, 300, TimeUnit.MILLISECONDS); @@ -309,7 +311,7 @@ public void OneShot_interrupt_stops_emitting_logs() { // interrupt the on-going process that read many lines more than MAX_LINES fakeLogcat = new FakeLogcat(550, 549); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, null, capture); destroyWorkerAfter(watcher, 300, TimeUnit.MILLISECONDS); @@ -350,7 +352,7 @@ private static class CaptureCallback implements LogcatProcess.Callback { private final Map finished = new HashMap<>(); @Override - public void onStarted(String processId) { + public void onStarted(@NonNull String processId) { if (captured.containsKey(processId)) { throw new IllegalStateException("only unique process id is allowed"); } @@ -360,14 +362,15 @@ public void onStarted(String processId) { @Override public void emit( - String processId, - ArrayList logcatLines + @NonNull String processId, + @NonNull ArrayList logcatLines, + @Nullable String captureId ) { captured.get(processId).add(logcatLines); } @Override - public void onFinished(String processId) { + public void onFinished(@NonNull String processId) { captured.put(processId, Collections.unmodifiableList(captured.get(processId))); finished.put(processId, true); } From 54e645fc03c20a9027457ee56fcd314f94359190 Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 15 Aug 2023 13:44:55 +0900 Subject: [PATCH 06/16] feat: Send background event as well --- .../java/com/deploygate/sdk/DeployGate.java | 21 ++++++++++++++++--- .../VisibilityLifecycleCallbacks.java | 9 ++++++++ .../deploygate/service/DeployGateEvent.java | 16 +++++++++++--- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 5fcbc14..352134c 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -84,12 +84,15 @@ public class DeployGate { public void onEvent( @NonNull String action, - @NonNull Bundle extras + @Nullable Bundle extras ) throws RemoteException { if (TextUtils.isEmpty(action)) { return; } + // ensure non-null + extras = extras != null ? extras : new Bundle(); + if (DeployGateEvent.ACTION_INIT.equals(action)) { onInitialized(extras.getBoolean(DeployGateEvent.EXTRA_IS_MANAGED, false), extras.getBoolean(DeployGateEvent.EXTRA_IS_AUTHORIZED, false), extras.getString(DeployGateEvent.EXTRA_LOGIN_USERNAME), extras.getString(DeployGateEvent.EXTRA_DISTRIBUTION_USER_NAME), extras.getBoolean(DeployGateEvent.EXTRA_IS_STOP_REQUESTED, false), extras.getString(DeployGateEvent.EXTRA_AUTHOR), extras.getInt(DeployGateEvent.EXTRA_CURRENT_REVISION, 0), extras.getString(DeployGateEvent.EXTRA_CURRENT_DISTRIBUTION_ID), extras.getString(DeployGateEvent.EXTRA_CURRENT_DISTRIBUTION_TITLE)); } else if (DeployGateEvent.ACTION_UPDATE_AVAILABLE.equals(action)) { @@ -195,8 +198,20 @@ public void onForeground( @NonNull TimeUnit timeUnit ) { Bundle extras = new Bundle(); - extras.putLong(DeployGateEvent.EXTRA_FOREGROUND_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); - invokeAction(DeployGateEvent.ACTION_GO_TO_FOREGROUND, extras); + extras.putLong(DeployGateEvent.EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); + extras.putInt(DeployGateEvent.EXTRA_VISIBILITY_EVENT_NAME, DeployGateEvent.Visibility.FOREGROUND); + invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras); + } + + @Override + public void onBackground( + long elapsedRealtime, + @NonNull TimeUnit timeUnit + ) { + Bundle extras = new Bundle(); + extras.putLong(DeployGateEvent.EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); + extras.putInt(DeployGateEvent.EXTRA_VISIBILITY_EVENT_NAME, DeployGateEvent.Visibility.BACKGROUND); + invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras); } }; diff --git a/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java b/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java index 2787b1e..c9f9b24 100644 --- a/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java +++ b/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java @@ -14,6 +14,7 @@ public final class VisibilityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { public interface OnVisibilityChangeListener { void onForeground(long elapsedRealtime, @NonNull TimeUnit timeUnit); + void onBackground(long elapsedRealtime, @NonNull TimeUnit timeUnit); } private int onResumeCount = 0; // this is manipulated from the single thread @@ -52,6 +53,14 @@ public void onActivityResumed(@NonNull Activity activity) { @Override public void onActivityPaused(@NonNull Activity activity) { onResumeCount = Math.max(onResumeCount - 1, 0); // cuz uint is unavailable. + + if (onResumeCount == 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + listener.onBackground(SystemClock.elapsedRealtimeNanos(), TimeUnit.NANOSECONDS); + } else { + listener.onBackground(SystemClock.elapsedRealtime(), TimeUnit.MILLISECONDS); + } + } } @Override diff --git a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java index b99f1b3..acd9ae8 100644 --- a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java +++ b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java @@ -25,7 +25,7 @@ public interface DeployGateEvent { public static final String ACTION_OPEN_APP_DETAIL = "openAppDetail"; public static final String ACTION_OPEN_COMMENTS = "openComments"; public static final String ACTION_COMPOSE_COMMENT = "composeComment"; - public static final String ACTION_GO_TO_FOREGROUND = "a.go-to-foreground"; + public static final String ACTION_VISIBILITY_EVENT = "a.visibility-event"; public static final String EXTRA_AUTHOR = "author"; public static final String EXTRA_EXPECTED_AUTHOR = "expectedAuthor"; @@ -93,8 +93,18 @@ public interface DeployGateEvent { public static final String EXTRA_CAPTURE_ID = "e.capture-id"; /** - * The elapsed real time since boot at the time when the app goes to foreground. + * A event name for the app goes to foreground/background. + */ + public static final String EXTRA_VISIBILITY_EVENT_NAME = "e.visibility-event-name"; + + /** + * The elapsed real time since boot at the time when the app goes to foreground/background. * this value must be nano times. */ - public static final String EXTRA_FOREGROUND_EVENT_ELAPSED_REAL_TIME_IN_NANOS = "e.foreground-event-elapsed-real-time"; + public static final String EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS = "e.visibility-event-elapsed-real-time"; + + interface Visibility { + int BACKGROUND = 0; + int FOREGROUND = 1; + } } From 5b154872142c2cdaf6427446f37d7f1ee642c2b0 Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 15 Aug 2023 13:56:04 +0900 Subject: [PATCH 07/16] refactor: Rename visibility-related events --- sdk/src/main/java/com/deploygate/sdk/DeployGate.java | 4 ++-- .../main/java/com/deploygate/service/DeployGateEvent.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 352134c..19ea44f 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -199,7 +199,7 @@ public void onForeground( ) { Bundle extras = new Bundle(); extras.putLong(DeployGateEvent.EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); - extras.putInt(DeployGateEvent.EXTRA_VISIBILITY_EVENT_NAME, DeployGateEvent.Visibility.FOREGROUND); + extras.putInt(DeployGateEvent.EXTRA_VISIBILITY_EVENT_TYPE, DeployGateEvent.VisibilityType.FOREGROUND); invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras); } @@ -210,7 +210,7 @@ public void onBackground( ) { Bundle extras = new Bundle(); extras.putLong(DeployGateEvent.EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); - extras.putInt(DeployGateEvent.EXTRA_VISIBILITY_EVENT_NAME, DeployGateEvent.Visibility.BACKGROUND); + extras.putInt(DeployGateEvent.EXTRA_VISIBILITY_EVENT_TYPE, DeployGateEvent.VisibilityType.BACKGROUND); invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras); } }; diff --git a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java index acd9ae8..f188c50 100644 --- a/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java +++ b/sdk/src/main/java/com/deploygate/service/DeployGateEvent.java @@ -93,9 +93,9 @@ public interface DeployGateEvent { public static final String EXTRA_CAPTURE_ID = "e.capture-id"; /** - * A event name for the app goes to foreground/background. + * A event type for the app goes to foreground/background. */ - public static final String EXTRA_VISIBILITY_EVENT_NAME = "e.visibility-event-name"; + public static final String EXTRA_VISIBILITY_EVENT_TYPE = "e.visibility-event-type"; /** * The elapsed real time since boot at the time when the app goes to foreground/background. @@ -103,7 +103,7 @@ public interface DeployGateEvent { */ public static final String EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS = "e.visibility-event-elapsed-real-time"; - interface Visibility { + interface VisibilityType { int BACKGROUND = 0; int FOREGROUND = 1; } From 9158187ce900b57be3307233d4233c6eac97b896 Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 15 Aug 2023 16:28:08 +0900 Subject: [PATCH 08/16] fix: visibility events were consumed before establishing a service connection --- .../java/com/deploygate/sdk/DeployGate.java | 56 ++++++++++++++++--- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 19ea44f..0ce241a 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -24,8 +24,11 @@ import com.deploygate.service.IDeployGateSdkService; import com.deploygate.service.IDeployGateSdkServiceCallback; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -47,6 +50,7 @@ public class DeployGate { private static final String ACTION_DEPLOYGATE_STARTED = "com.deploygate.action.ServiceStarted"; private static final String DEPLOYGATE_PACKAGE = "com.deploygate"; + private static final Object sPendingEventLock = new Object(); private static DeployGate sInstance; @@ -57,6 +61,7 @@ public class DeployGate { private final ILogcatInstructionSerializer mLogcatInstructionSerializer; private final CustomLogInstructionSerializer mCustomLogInstructionSerializer; private final HashSet mCallbacks; + private final HashMap mPendingEvents; private final String mExpectedAuthor; private String mAuthor; @@ -164,6 +169,9 @@ public void run() { mLogcatInstructionSerializer.connect(mRemoteService); mInitializedLatch.countDown(); + + // to release a lock as soon as possible. + flushPendingEvents(); } private void onUpdateArrived( @@ -192,6 +200,7 @@ public void run() { @SuppressWarnings("FieldCanBeLocal") @NonNull private final VisibilityLifecycleCallbacks.OnVisibilityChangeListener mOnVisibilityChangeListener = new VisibilityLifecycleCallbacks.OnVisibilityChangeListener() { + @Override public void onForeground( long elapsedRealtime, @@ -200,7 +209,7 @@ public void onForeground( Bundle extras = new Bundle(); extras.putLong(DeployGateEvent.EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); extras.putInt(DeployGateEvent.EXTRA_VISIBILITY_EVENT_TYPE, DeployGateEvent.VisibilityType.FOREGROUND); - invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras); + invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras, true); } @Override @@ -211,7 +220,7 @@ public void onBackground( Bundle extras = new Bundle(); extras.putLong(DeployGateEvent.EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); extras.putInt(DeployGateEvent.EXTRA_VISIBILITY_EVENT_TYPE, DeployGateEvent.VisibilityType.BACKGROUND); - invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras); + invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras, true); } }; @@ -261,13 +270,13 @@ private DeployGate( HostApp hostApp ) { mApplicationContext = applicationContext; - VisibilityLifecycleCallbacks mVisibilityLifecycleCallbacks = new VisibilityLifecycleCallbacks(mOnVisibilityChangeListener); mDeployGateClient = new DeployGateClient(applicationContext, DEPLOYGATE_PACKAGE); mHostApp = hostApp; mHandler = new Handler(); mLogcatInstructionSerializer = mHostApp.canUseLogcat ? new LogcatInstructionSerializer(mHostApp.packageName) : ILogcatInstructionSerializer.NULL_INSTANCE; mCustomLogInstructionSerializer = new CustomLogInstructionSerializer(mHostApp.packageName, customLogConfiguration); - mCallbacks = new HashSet(); + mCallbacks = new HashSet<>(); + mPendingEvents = new HashMap<>(); mExpectedAuthor = author; prepareBroadcastReceiver(); @@ -277,7 +286,7 @@ private DeployGate( } mInitializedLatch = new CountDownLatch(1); - ((Application) applicationContext).registerActivityLifecycleCallbacks(mVisibilityLifecycleCallbacks); + ((Application) applicationContext).registerActivityLifecycleCallbacks(new VisibilityLifecycleCallbacks(mOnVisibilityChangeListener)); initService(true); } @@ -357,11 +366,27 @@ private void requestServiceInit(final boolean isBoot) { } } + /** + * Send an event to the client application + * + * @param action to be sent + * @param extras to be sent + * @param allowPending Allow queueing events to send them after a service connection is established (since 4.6.0) + */ private void invokeAction( @NonNull String action, - @Nullable Bundle extras + @Nullable Bundle extras, + boolean allowPending ) { + extras = extras != null ? extras : new Bundle(); + if (mRemoteService == null) { + if (allowPending) { + synchronized (sPendingEventLock) { + mPendingEvents.put(action, extras); + } + } + return; } try { @@ -371,6 +396,19 @@ private void invokeAction( } } + private void flushPendingEvents() { + // cannot re-enqueue events for now + synchronized (sPendingEventLock) { + Iterator> iterator = mPendingEvents.entrySet().iterator(); + + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + invokeAction(entry.getKey(), entry.getValue(), false); + iterator.remove(); + } + } + } + /** * Clear the initiated DeployGate instance. *

@@ -1230,7 +1268,7 @@ public static void installUpdate() { return; } - sInstance.invokeAction(DeployGateEvent.ACTION_INSTALL_UPDATE, null); + sInstance.invokeAction(DeployGateEvent.ACTION_INSTALL_UPDATE, null, false); } /** @@ -1246,7 +1284,7 @@ public static void openComments() { return; } - sInstance.invokeAction(DeployGateEvent.ACTION_OPEN_COMMENTS, null); + sInstance.invokeAction(DeployGateEvent.ACTION_OPEN_COMMENTS, null, false); } /** @@ -1279,7 +1317,7 @@ public static void composeComment(String defaultComment) { Bundle extras = new Bundle(); extras.putString(DeployGateEvent.EXTRA_COMMENT, defaultComment); - sInstance.invokeAction(DeployGateEvent.ACTION_COMPOSE_COMMENT, extras); + sInstance.invokeAction(DeployGateEvent.ACTION_COMPOSE_COMMENT, extras, false); } /** From b1f18c13de7e56081766ce2e57772dfff8c481e2 Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 15 Aug 2023 17:33:49 +0900 Subject: [PATCH 09/16] test: Address modified signatures and remove androidx dependency for now --- sdk.build.gradle | 2 - .../java/com/deploygate/sdk/DeployGate.java | 18 ++-- .../sdk/ILogcatInstructionSerializer.java | 13 ++- .../sdk/LogcatInstructionSerializer.java | 36 +++----- .../com/deploygate/sdk/LogcatProcess.java | 25 +++--- .../com/deploygate/sdk/SendLogcatRequest.java | 35 ++------ .../VisibilityLifecycleCallbacks.java | 28 +++---- .../sdk/LogcatInstructionSerializerTest.java | 20 ++--- .../com/deploygate/sdk/LogcatProcessTest.java | 18 ++-- .../deploygate/sdk/SendLogcatRequestTest.java | 83 ++++++++++++------- 10 files changed, 124 insertions(+), 154 deletions(-) diff --git a/sdk.build.gradle b/sdk.build.gradle index f2001a8..6dab261 100644 --- a/sdk.build.gradle +++ b/sdk.build.gradle @@ -40,8 +40,6 @@ android { } dependencies { - implementation "androidx.annotation:annotation:1.6.0" - testImplementation 'androidx.test:runner:1.5.2' testImplementation 'androidx.test.ext:junit:1.1.5' testImplementation 'org.robolectric:robolectric:4.10.3' diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 0ce241a..0eb64db 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -15,9 +15,6 @@ import android.text.TextUtils; import android.util.Log; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import com.deploygate.sdk.internal.VisibilityLifecycleCallbacks; import com.deploygate.sdk.internal.Logger; import com.deploygate.service.DeployGateEvent; @@ -88,8 +85,8 @@ public class DeployGate { private final IDeployGateSdkServiceCallback mRemoteCallback = new IDeployGateSdkServiceCallback.Stub() { public void onEvent( - @NonNull String action, - @Nullable Bundle extras + String action, + Bundle extras ) throws RemoteException { if (TextUtils.isEmpty(action)) { return; @@ -198,13 +195,12 @@ public void run() { }; @SuppressWarnings("FieldCanBeLocal") - @NonNull private final VisibilityLifecycleCallbacks.OnVisibilityChangeListener mOnVisibilityChangeListener = new VisibilityLifecycleCallbacks.OnVisibilityChangeListener() { @Override public void onForeground( long elapsedRealtime, - @NonNull TimeUnit timeUnit + TimeUnit timeUnit ) { Bundle extras = new Bundle(); extras.putLong(DeployGateEvent.EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); @@ -215,7 +211,7 @@ public void onForeground( @Override public void onBackground( long elapsedRealtime, - @NonNull TimeUnit timeUnit + TimeUnit timeUnit ) { Bundle extras = new Bundle(); extras.putLong(DeployGateEvent.EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); @@ -228,7 +224,7 @@ private void requestOneshotLogcat() { onOneshotLogcat(null); } - private void onOneshotLogcat(@Nullable String captureId) { + private void onOneshotLogcat(String captureId) { mLogcatInstructionSerializer.requestOneshotLogcat(captureId); } @@ -374,8 +370,8 @@ private void requestServiceInit(final boolean isBoot) { * @param allowPending Allow queueing events to send them after a service connection is established (since 4.6.0) */ private void invokeAction( - @NonNull String action, - @Nullable Bundle extras, + String action, + Bundle extras, boolean allowPending ) { extras = extras != null ? extras : new Bundle(); diff --git a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java index 973d0fb..52f6e32 100644 --- a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java @@ -1,8 +1,5 @@ package com.deploygate.sdk; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import com.deploygate.sdk.internal.Logger; import com.deploygate.service.IDeployGateSdkService; @@ -14,7 +11,7 @@ interface ILogcatInstructionSerializer { * @param service * the latest service connection */ - void connect(@NonNull IDeployGateSdkService service); + void connect(IDeployGateSdkService service); /** * Release a service connection and cancel all pending instructions and on-going instruction. @@ -26,13 +23,13 @@ interface ILogcatInstructionSerializer { * * @param captureId this is nullable. Set to non-null if this logcat is for a capture. */ - boolean requestOneshotLogcat(@Nullable String captureId); + boolean requestOneshotLogcat(String captureId); /** * Create and enqueue a request to start sending streamed logcat */ boolean requestStreamedLogcat( - @Nullable String sessionKey + String sessionKey ); /** @@ -51,7 +48,7 @@ boolean requestStreamedLogcat( ILogcatInstructionSerializer NULL_INSTANCE = new ILogcatInstructionSerializer() { @Override - public void connect(@NonNull IDeployGateSdkService service) { + public void connect(IDeployGateSdkService service) { Logger.d("Logcat (no-op): connect"); } @@ -67,7 +64,7 @@ public boolean requestOneshotLogcat(String captureId) { } @Override - public boolean requestStreamedLogcat(@Nullable String sessionKey) { + public boolean requestStreamedLogcat(String sessionKey) { Logger.d("Logcat (no-op): requestStreamedLogcat(%s)", sessionKey); return false; } diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java index bc7e591..eba54ad 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java @@ -11,9 +11,6 @@ import android.text.TextUtils; import android.util.Pair; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import com.deploygate.sdk.internal.Logger; import com.deploygate.service.DeployGateEvent; import com.deploygate.service.IDeployGateSdkService; @@ -34,37 +31,31 @@ class LogcatInstructionSerializer implements ILogcatInstructionSerializer { static final int WHAT_SEND_LOGCAT = 0x20; static final int WHAT_ADD_LOGCAT_CHUNK = 0x30; - @NonNull private static final Object LOCK = new Object(); - @NonNull private final String packageName; /** * nullable if logcat is not supported on this device */ - @Nullable private final LogcatProcess logcatProcess; /** * nullable if logcat is not supported on this device */ @SuppressWarnings("FieldCanBeLocal") - @NonNull private final HandlerThread thread; /** * NonNull if prepared once, however this is always null Logcat is not supported */ - @Nullable private LogcatHandler handler; private boolean isEnabled; - @Nullable private volatile IDeployGateSdkService service; LogcatInstructionSerializer( - @NonNull String packageName + String packageName ) { if (TextUtils.isEmpty(packageName)) { throw new IllegalArgumentException("packageName must be present"); @@ -75,16 +66,16 @@ class LogcatInstructionSerializer implements ILogcatInstructionSerializer { this.logcatProcess = new LogcatProcess(new LogcatProcess.Callback() { @Override - public void onStarted(@NonNull String processId) { + public void onStarted(String processId) { //noinspection ConstantConditions handler.enqueueSendLogcatMessageInstruction(SendLogcatRequest.createBeginning(processId)); } @Override public void emit( - @NonNull String processId, - @NonNull ArrayList logcatLines, - @Nullable String captureId + String processId, + ArrayList logcatLines, + String captureId ) { ensureHandlerPrepared(); @@ -93,7 +84,7 @@ public void emit( } @Override - public void onFinished(@NonNull String processId) { + public void onFinished(String processId) { //noinspection ConstantConditions handler.enqueueSendLogcatMessageInstruction(SendLogcatRequest.createTermination(processId)); } @@ -103,7 +94,7 @@ public void onFinished(@NonNull String processId) { } @Override - public final synchronized void connect(@NonNull IDeployGateSdkService service) { + public final synchronized void connect(IDeployGateSdkService service) { //noinspection ConstantConditions if (service == null) { throw new IllegalArgumentException("service must not be null"); @@ -121,12 +112,12 @@ public final void disconnect() { } @Override - public final synchronized boolean requestOneshotLogcat(@Nullable String captureId) { + public final synchronized boolean requestOneshotLogcat(String captureId) { return requestLogcat(null, captureId); } @Override - public boolean requestStreamedLogcat(@Nullable String sessionKey) { + public boolean requestStreamedLogcat(String sessionKey) { if (TextUtils.isEmpty(sessionKey)) { Logger.w("non-blank stream key is required"); return false; @@ -182,7 +173,7 @@ public final boolean hasServiceConnection() { * otherwise {@link LogcatInstructionSerializer#SEND_LOGCAT_RESULT_FAILURE_RETRY_EXCEEDED} */ int sendSingleChunk( - @NonNull SendLogcatRequest request + SendLogcatRequest request ) { IDeployGateSdkService service = this.service; @@ -233,7 +224,7 @@ int sendSingleChunk( * * @return true if new process has lauched */ - private boolean requestLogcat(@Nullable String streamSessionKey) { + private boolean requestLogcat(String streamSessionKey) { return requestLogcat(streamSessionKey, null); } @@ -242,7 +233,7 @@ private boolean requestLogcat(@Nullable String streamSessionKey) { * @param captureId nullable. * @return true if new process has lauched */ - private boolean requestLogcat(@Nullable String streamSessionKey, @Nullable String captureId) { + private boolean requestLogcat(String streamSessionKey, String captureId) { ensureHandlerPrepared(); if (!isEnabled || logcatProcess == null) { @@ -289,7 +280,7 @@ private void ensureHandlerPrepared() { */ private int sendChunkedLogcats( int splitTimes, - @NonNull LinkedList pendingRequests + LinkedList pendingRequests ) { SendLogcatRequest request = pendingRequests.removeFirst(); @@ -317,7 +308,6 @@ private int sendChunkedLogcats( /** * @return the handler instance */ - @NonNull Handler getHandler() { ensureHandlerPrepared(); //noinspection ConstantConditions diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java index c28fa61..5ad94de 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java @@ -3,9 +3,6 @@ import android.os.Build; import android.util.Pair; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import com.deploygate.sdk.internal.Logger; import com.deploygate.sdk.internal.annotations.Experimental; @@ -31,7 +28,7 @@ interface Callback { void emit( String processId, ArrayList logcatLines, - @Nullable String captureId + String captureId ); void onFinished(String processId); @@ -63,7 +60,7 @@ void emit( */ Pair execute( @Experimental String streamSessionKey, - @Nullable String captureId + String captureId ) { Pair ids; @@ -124,20 +121,20 @@ static class LogcatWatcher implements Runnable { private static final int STATE_INTERRUPTED = 2; private static final int STATE_FINISHED = 3; - @NonNull private final String processId; + private final String processId; private final boolean isOneShot; - @Nullable private final String captureId; - @NonNull private final WeakReference callback; - @NonNull private final AtomicReference processRef; - @NonNull private final AtomicInteger state; + private final String captureId; + private final WeakReference callback; + private final AtomicReference processRef; + private final AtomicInteger state; LogcatWatcher( - @Experimental @Nullable String streamSessionKey, - @Nullable String captureId, - @NonNull Callback callback + @Experimental String streamSessionKey, + String captureId, + Callback callback ) { if (streamSessionKey != null && captureId != null) { - throw new IllegalArgumentException("either of stream session key and capture id can be present"); + throw new IllegalArgumentException("streaming and capture cannot be specified at once"); } this.processId = streamSessionKey != null ? streamSessionKey : ClientId.generate(); diff --git a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java index 92ad634..7c47162 100644 --- a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java +++ b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java @@ -2,8 +2,6 @@ import android.os.Bundle; -import androidx.annotation.Nullable; - import com.deploygate.sdk.internal.Logger; import com.deploygate.service.DeployGateEvent; @@ -33,25 +31,18 @@ private String label() { } public static SendLogcatRequest createTermination(String processId) { - return new SendLogcatRequest(processId, new ArrayList(), Position.Termination); + return new SendLogcatRequest(processId, new ArrayList(), Position.Termination, null); } public static SendLogcatRequest createBeginning(String processId) { - return new SendLogcatRequest(processId, new ArrayList(), Position.Beginning); + return new SendLogcatRequest(processId, new ArrayList(), Position.Beginning, null); } public final ArrayList lines; public final Position position; - @Nullable public final String captureId; + public final String captureId; private int retryCount; - SendLogcatRequest( - String pid, - List lines - ) { - this(pid, lines, Position.Content); - } - SendLogcatRequest( String pid, List lines, @@ -60,22 +51,6 @@ public static SendLogcatRequest createBeginning(String processId) { this(pid, lines, Position.Content, captureId); } - /** - * @param pid - * a process id. non-null - * @param lines - * logcat contents if available. Zero value is an empty list. - * @param position - * a position of this request. non-null - */ - private SendLogcatRequest( - String pid, - List lines, - Position position - ) { - this(pid, lines, position, null); - } - /** * @param pid a process id. non-null * @param lines logcat contents if available. Zero value is an empty list. @@ -86,7 +61,7 @@ private SendLogcatRequest( String pid, List lines, Position position, - @Nullable String captureId + String captureId ) { super(pid); this.lines = lines instanceof ArrayList ? (ArrayList) lines : new ArrayList<>(lines); @@ -122,7 +97,7 @@ List splitInto(int count) { for (int i = 0, offset = 0, step = size / count; i < count; i++, offset += step) { final int endIndex = (i == count - 1) ? size : offset + step; - splits.add(new SendLogcatRequest(gid, lines.subList(offset, endIndex), Position.Content)); + splits.add(new SendLogcatRequest(gid, lines.subList(offset, endIndex), Position.Content, captureId)); } return splits; diff --git a/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java b/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java index c9f9b24..a5c813f 100644 --- a/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java +++ b/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java @@ -6,41 +6,37 @@ import android.os.Bundle; import android.os.SystemClock; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import java.util.concurrent.TimeUnit; public final class VisibilityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { public interface OnVisibilityChangeListener { - void onForeground(long elapsedRealtime, @NonNull TimeUnit timeUnit); - void onBackground(long elapsedRealtime, @NonNull TimeUnit timeUnit); + void onForeground(long elapsedRealtime, TimeUnit timeUnit); + void onBackground(long elapsedRealtime, TimeUnit timeUnit); } private int onResumeCount = 0; // this is manipulated from the single thread - @NonNull private final OnVisibilityChangeListener listener; - public VisibilityLifecycleCallbacks(@NonNull OnVisibilityChangeListener listener) { + public VisibilityLifecycleCallbacks(OnVisibilityChangeListener listener) { this.listener = listener; } @Override public void onActivityCreated( - @NonNull Activity activity, - @Nullable Bundle savedInstanceState + Activity activity, + Bundle savedInstanceState ) { // no-op } @Override - public void onActivityStarted(@NonNull Activity activity) { + public void onActivityStarted(Activity activity) { // no-op } @Override - public void onActivityResumed(@NonNull Activity activity) { + public void onActivityResumed(Activity activity) { onResumeCount++; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { @@ -51,7 +47,7 @@ public void onActivityResumed(@NonNull Activity activity) { } @Override - public void onActivityPaused(@NonNull Activity activity) { + public void onActivityPaused(Activity activity) { onResumeCount = Math.max(onResumeCount - 1, 0); // cuz uint is unavailable. if (onResumeCount == 0) { @@ -64,20 +60,20 @@ public void onActivityPaused(@NonNull Activity activity) { } @Override - public void onActivityStopped(@NonNull Activity activity) { + public void onActivityStopped(Activity activity) { // no-op } @Override public void onActivitySaveInstanceState( - @NonNull Activity activity, - @NonNull Bundle outState + Activity activity, + Bundle outState ) { // no-op } @Override - public void onActivityDestroyed(@NonNull Activity activity) { + public void onActivityDestroyed(Activity activity) { // no-op } } diff --git a/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java b/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java index 8b2dc62..ca5669f 100644 --- a/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java @@ -104,9 +104,9 @@ public void sendSingleChunk_always_returns_retriable_status_if_service_is_none() instructionSerializer.requestOneshotLogcat(null); - SendLogcatRequest chunk1 = new SendLogcatRequest("tid1", new ArrayList<>(Arrays.asList("line1", "line2", "line3"))); - SendLogcatRequest chunk2 = new SendLogcatRequest("tid2", new ArrayList<>(Arrays.asList("line4", "line5", "line6"))); - SendLogcatRequest chunk3 = new SendLogcatRequest("tid3", new ArrayList<>(Arrays.asList("line7", "line8", "line9"))); + SendLogcatRequest chunk1 = new SendLogcatRequest("tid1", new ArrayList<>(Arrays.asList("line1", "line2", "line3")), null); + SendLogcatRequest chunk2 = new SendLogcatRequest("tid2", new ArrayList<>(Arrays.asList("line4", "line5", "line6")), null); + SendLogcatRequest chunk3 = new SendLogcatRequest("tid3", new ArrayList<>(Arrays.asList("line7", "line8", "line9")), null); doNothing().when(service).sendEvent(anyString(), anyString(), any(Bundle.class)); @@ -123,9 +123,9 @@ public void sendSingleChunk_always_returns_retriable_status_if_service_is_none_a instructionSerializer.requestOneshotLogcat("brabra"); - SendLogcatRequest chunk1 = new SendLogcatRequest("tid1", new ArrayList<>(Arrays.asList("line1", "line2", "line3"))); - SendLogcatRequest chunk2 = new SendLogcatRequest("tid2", new ArrayList<>(Arrays.asList("line4", "line5", "line6"))); - SendLogcatRequest chunk3 = new SendLogcatRequest("tid3", new ArrayList<>(Arrays.asList("line7", "line8", "line9"))); + SendLogcatRequest chunk1 = new SendLogcatRequest("tid1", new ArrayList<>(Arrays.asList("line1", "line2", "line3")), "brabra"); + SendLogcatRequest chunk2 = new SendLogcatRequest("tid2", new ArrayList<>(Arrays.asList("line4", "line5", "line6")), "brabra"); + SendLogcatRequest chunk3 = new SendLogcatRequest("tid3", new ArrayList<>(Arrays.asList("line7", "line8", "line9")), "brabra"); doNothing().when(service).sendEvent(anyString(), anyString(), any(Bundle.class)); @@ -143,10 +143,10 @@ public void sendSingleChunk_uses_retry_barrier() throws RemoteException { Shadows.shadowOf(instructionSerializer.getHandler().getLooper()).pause(); - SendLogcatRequest noIssue = new SendLogcatRequest("noIssue", new ArrayList<>(Arrays.asList("line1", "line2", "line3"))); - SendLogcatRequest successAfterRetries = new SendLogcatRequest("successAfterRetries", new ArrayList<>(Arrays.asList("line4", "line5", "line6"))); - SendLogcatRequest retryExceeded = new SendLogcatRequest("retryExceeded", new ArrayList<>(Arrays.asList("line7", "line8", "line9"))); - SendLogcatRequest chunkRequest = new SendLogcatRequest("chunkRequest", new ArrayList<>(Arrays.asList("line10", "line11", "line12"))); + SendLogcatRequest noIssue = new SendLogcatRequest("noIssue", new ArrayList<>(Arrays.asList("line1", "line2", "line3")), null); + SendLogcatRequest successAfterRetries = new SendLogcatRequest("successAfterRetries", new ArrayList<>(Arrays.asList("line4", "line5", "line6")), null); + SendLogcatRequest retryExceeded = new SendLogcatRequest("retryExceeded", new ArrayList<>(Arrays.asList("line7", "line8", "line9")), null); + SendLogcatRequest chunkRequest = new SendLogcatRequest("chunkRequest", new ArrayList<>(Arrays.asList("line10", "line11", "line12")), null); SendLogcatRequest beginningRequest = SendLogcatRequest.createBeginning("beginningRequest"); SendLogcatRequest terminationRequest = SendLogcatRequest.createTermination("terminationRequest"); diff --git a/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java b/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java index 0a29ebb..11b8a2d 100644 --- a/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java @@ -91,7 +91,7 @@ public void nonOneShot_emits_multiple_log_chunks() { try { fakeLogcat = new FakeLogcat(501); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks2", null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bsk2", null, capture); watcher.run(); @@ -196,17 +196,17 @@ public void OneShot_emits_single_log_chunk() { @Test(timeout = 3000L) public void nonOneShot_interrupt_stops_later_emits() { - CaptureCallback capture = new CaptureCallback(); + CaptureCallback callback = new CaptureCallback(); try { // call the method for the finished process fakeLogcat = new FakeLogcat(10); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks1", null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks1", null, callback); watcher.run(); - List> linesList = capture.captured.get(watcher.getProcessId()); + List> linesList = callback.captured.get(watcher.getProcessId()); Truth.assertThat(linesList).hasSize(1); Truth.assertThat(linesList.get(0)).hasSize(10); @@ -223,13 +223,13 @@ public void nonOneShot_interrupt_stops_later_emits() { // interrupt the on-going process that read lines less than MAX_LINES fakeLogcat = new FakeLogcat(20, 10); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks2", "cap2", capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bsk2", null, callback); destroyWorkerAfter(watcher, 300, TimeUnit.MILLISECONDS); watcher.run(); - List> linesList = capture.captured.get(watcher.getProcessId()); + List> linesList = callback.captured.get(watcher.getProcessId()); Truth.assertThat(linesList).isEmpty(); } finally { @@ -242,7 +242,7 @@ public void nonOneShot_interrupt_stops_later_emits() { // interrupt the on-going process that read many lines more than MAX_LINES fakeLogcat = new FakeLogcat(550, 549); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bks3", null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bsk3", null, callback); destroyWorkerAfter(watcher, 500, TimeUnit.MILLISECONDS); @@ -250,7 +250,7 @@ public void nonOneShot_interrupt_stops_later_emits() { List generatedLines = fakeLogcat.getGeneratedLines(); - List> linesList = capture.captured.get(watcher.getProcessId()); + List> linesList = callback.captured.get(watcher.getProcessId()); // emit the first chunk but second chunk Truth.assertThat(linesList).hasSize(1); @@ -271,7 +271,7 @@ public void OneShot_interrupt_stops_emitting_logs() { // call the method for the finished process fakeLogcat = new FakeLogcat(10); - LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher(null, null, capture); + LogcatProcess.LogcatWatcher watcher = new LogcatProcess.LogcatWatcher("bsk1", null, capture); watcher.run(); diff --git a/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java b/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java index 5bc5dc2..c075d78 100644 --- a/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java @@ -19,6 +19,7 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Objects; import static com.google.common.truth.Truth.assertAbout; @@ -37,21 +38,25 @@ public void apply() throws Throwable { SendLogcatRequest beginning = SendLogcatRequest.createBeginning("bsk1"); - BundleSubject.assertThat(beginning.toExtras()).isEqualTo(createLogExtra("bsk1", "unique_id", new ArrayList(), "beginning")); + BundleSubject.assertThat(beginning.toExtras()).isEqualTo(createLogExtra("bsk1", "unique_id", new ArrayList(), "beginning", null)); - SendLogcatRequest content = new SendLogcatRequest("bsk2", new ArrayList<>(Arrays.asList("1", "2"))); + SendLogcatRequest content = new SendLogcatRequest("bsk2", new ArrayList<>(Arrays.asList("1", "2")), null); - BundleSubject.assertThat(content.toExtras()).isEqualTo(createLogExtra("bsk2", "unique_id", new ArrayList<>(Arrays.asList("1", "2")), "content")); + BundleSubject.assertThat(content.toExtras()).isEqualTo(createLogExtra("bsk2", "unique_id", new ArrayList<>(Arrays.asList("1", "2")), "content", null)); SendLogcatRequest termination = SendLogcatRequest.createTermination("bsk3"); - BundleSubject.assertThat(termination.toExtras()).isEqualTo(createLogExtra("bsk3", "unique_id", new ArrayList(), "termination")); + BundleSubject.assertThat(termination.toExtras()).isEqualTo(createLogExtra("bsk3", "unique_id", new ArrayList(), "termination", null)); + + SendLogcatRequest capture = new SendLogcatRequest("bsk4", new ArrayList<>(Arrays.asList("1")), "capture_id"); + + BundleSubject.assertThat(capture.toExtras()).isEqualTo(createLogExtra("bsk4", "unique_id", new ArrayList<>(Arrays.asList("1")), "content", "capture_id")); } } @Test public void splitInto_create_n_sublist() { - SendLogcatRequest request = new SendLogcatRequest("bsk", arrayListOf(0, 10)); + SendLogcatRequest request = new SendLogcatRequest("bsk", arrayListOf(0, 10), null); List singleRequests = request.splitInto(1); @@ -61,39 +66,47 @@ public void splitInto_create_n_sublist() { List twoRequests = request.splitInto(2); Truth.assertThat(twoRequests).hasSize(2); - SendLogcatRequestSubject.assertThat(twoRequests.get(0)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(0, 5)); - SendLogcatRequestSubject.assertThat(twoRequests.get(1)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(5, 5)); + SendLogcatRequestSubject.assertThat(twoRequests.get(0)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(0, 5), null); + SendLogcatRequestSubject.assertThat(twoRequests.get(1)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(5, 5), null); List threeRequests = request.splitInto(3); Truth.assertThat(threeRequests).hasSize(3); - SendLogcatRequestSubject.assertThat(threeRequests.get(0)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(0, 3)); - SendLogcatRequestSubject.assertThat(threeRequests.get(1)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(3, 3)); - SendLogcatRequestSubject.assertThat(threeRequests.get(2)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(6, 4)); + SendLogcatRequestSubject.assertThat(threeRequests.get(0)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(0, 3), null); + SendLogcatRequestSubject.assertThat(threeRequests.get(1)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(3, 3), null); + SendLogcatRequestSubject.assertThat(threeRequests.get(2)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(6, 4), null); List nineRequests = request.splitInto(9); Truth.assertThat(nineRequests).hasSize(9); - SendLogcatRequestSubject.assertThat(nineRequests.get(0)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(0, 1)); - SendLogcatRequestSubject.assertThat(nineRequests.get(1)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(1, 1)); - SendLogcatRequestSubject.assertThat(nineRequests.get(2)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(2, 1)); - SendLogcatRequestSubject.assertThat(nineRequests.get(3)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(3, 1)); - SendLogcatRequestSubject.assertThat(nineRequests.get(4)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(4, 1)); - SendLogcatRequestSubject.assertThat(nineRequests.get(5)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(5, 1)); - SendLogcatRequestSubject.assertThat(nineRequests.get(6)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(6, 1)); - SendLogcatRequestSubject.assertThat(nineRequests.get(7)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(7, 1)); - SendLogcatRequestSubject.assertThat(nineRequests.get(8)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(8, 2)); + SendLogcatRequestSubject.assertThat(nineRequests.get(0)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(0, 1), null); + SendLogcatRequestSubject.assertThat(nineRequests.get(1)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(1, 1), null); + SendLogcatRequestSubject.assertThat(nineRequests.get(2)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(2, 1), null); + SendLogcatRequestSubject.assertThat(nineRequests.get(3)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(3, 1), null); + SendLogcatRequestSubject.assertThat(nineRequests.get(4)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(4, 1), null); + SendLogcatRequestSubject.assertThat(nineRequests.get(5)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(5, 1), null); + SendLogcatRequestSubject.assertThat(nineRequests.get(6)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(6, 1), null); + SendLogcatRequestSubject.assertThat(nineRequests.get(7)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(7, 1), null); + SendLogcatRequestSubject.assertThat(nineRequests.get(8)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(8, 2), null); List overSplitRequests = request.splitInto(11); Truth.assertThat(overSplitRequests).hasSize(10); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(0)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(0, 1)); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(1)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(1, 1)); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(2)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(2, 1)); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(3)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(3, 1)); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(4)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(4, 1)); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(5)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(5, 1)); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(6)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(6, 1)); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(7)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(7, 1)); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(8)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(8, 1)); - SendLogcatRequestSubject.assertThat(overSplitRequests.get(9)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(9, 1)); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(0)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(0, 1), null); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(1)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(1, 1), null); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(2)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(2, 1), null); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(3)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(3, 1), null); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(4)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(4, 1), null); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(5)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(5, 1), null); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(6)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(6, 1), null); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(7)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(7, 1), null); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(8)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(8, 1), null); + SendLogcatRequestSubject.assertThat(overSplitRequests.get(9)).isSameInBundle(request.gid, SendLogcatRequest.Position.Content, arrayListOf(9, 1), null); + + SendLogcatRequest requestWithCapture = new SendLogcatRequest("bsk2", arrayListOf(0, 10), "capture_id"); + + List threeRequestsWithCapture = requestWithCapture.splitInto(3); + Truth.assertThat(threeRequestsWithCapture).hasSize(3); + SendLogcatRequestSubject.assertThat(threeRequestsWithCapture.get(0)).isSameInBundle(requestWithCapture.gid, SendLogcatRequest.Position.Content, arrayListOf(0, 3), "capture_id"); + SendLogcatRequestSubject.assertThat(threeRequestsWithCapture.get(1)).isSameInBundle(requestWithCapture.gid, SendLogcatRequest.Position.Content, arrayListOf(3, 3), "capture_id"); + SendLogcatRequestSubject.assertThat(threeRequestsWithCapture.get(2)).isSameInBundle(requestWithCapture.gid, SendLogcatRequest.Position.Content, arrayListOf(6, 4), "capture_id"); } @Test @@ -130,13 +143,15 @@ private static Bundle createLogExtra( String gid, String cid, ArrayList lines, - String positionLabel + String positionLabel, + String captureId ) { Bundle bundle = new Bundle(); bundle.putString("e.gid", gid); bundle.putString("e.cid", cid); bundle.putStringArrayList("log", lines); bundle.putString("e.bundle-position", positionLabel); + bundle.putString("e.capture-id", captureId); return bundle; } @@ -177,7 +192,7 @@ protected SendLogcatRequestSubject( this.actual = actual; } - public void isSameInBundle(String expectedKey, SendLogcatRequest.Position expectedPosition, List expectedLines) { + public void isSameInBundle(String expectedKey, SendLogcatRequest.Position expectedPosition, List expectedLines, String captureId) { if (!actual.gid.equals(expectedKey)) { failWithActual(Fact.simpleFact(String.format(Locale.US, "%s is expected of %s", expectedKey, toString(actual)))); return; @@ -199,6 +214,10 @@ public void isSameInBundle(String expectedKey, SendLogcatRequest.Position expect return; } } + + if (!Objects.equals(actual.captureId, captureId)) { + failWithActual(Fact.simpleFact(String.format(Locale.US, "%s is expected to be exactly included in %s",actual.captureId, captureId))); + } } @Override @@ -225,6 +244,8 @@ private static String toString(SendLogcatRequest request) { builder.append(request.position.name()); builder.append(", lines=["); builder.append(String.join(", ", request.lines)); + builder.append(", capture_id="); + builder.append(request.captureId); builder.append("]"); builder.append(" }"); return builder.toString(); From e527905c86ff0a1425f4bf8cf4ceb0b0a1db007a Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 15 Aug 2023 21:49:59 +0900 Subject: [PATCH 10/16] feat: Use 4.6.0-alpha01 --- README.md | 8 ++++++++ build.gradle | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 463371d..d4c2cfd 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,14 @@ Unless required by applicable law or agreed to in writing, software distributed - Add new tests for your changes - Make sure all tests are passed +```bash +# help: build and install artifacts into your local maven repo +./gradlew clean \ + sdk:verifyBytecodeVersionRelease sdkMock:verifyBytecodeVersionRelease \ + sdk:publishReleasePublicationToMavenLocal sdkMock:publishReleasePublicationToMavenLocal \ + --stacktrace +``` + ### sdk - Consider if we should use external libraries carefully diff --git a/build.gradle b/build.gradle index fd93ca5..859bd99 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. ext { - releaseVersion = '4.5.0' + releaseVersion = '4.6.0-alpha01' } buildscript { From 3f26e5ffb99bafbc433919c2eecb2b8bb45f089c Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 15 Aug 2023 22:05:14 +0900 Subject: [PATCH 11/16] fmt: Apply codestyle --- .../java/com/deploygate/sdk/DeployGate.java | 13 ++++++---- .../sdk/ILogcatInstructionSerializer.java | 3 ++- .../sdk/LogcatInstructionSerializer.java | 14 +++++++---- .../com/deploygate/sdk/LogcatProcess.java | 16 ++++++------- .../com/deploygate/sdk/SendLogcatRequest.java | 12 ++++++---- .../VisibilityLifecycleCallbacks.java | 11 +++++++-- .../CustomLogInstructionSerializerTest.java | 20 ++++++++-------- .../deploygate/sdk/DeployGateClientTest.java | 4 ++-- .../sdk/DeployGateInterfaceTest.java | 4 ++-- .../java/com/deploygate/sdk/HostAppTest.java | 4 ++-- .../sdk/LogcatInstructionSerializerTest.java | 24 +++++++++---------- .../com/deploygate/sdk/LogcatProcessTest.java | 6 ++--- .../deploygate/sdk/SendLogcatRequestTest.java | 18 ++++++++++---- .../deploygate/sdk/mockito/BundleMatcher.java | 4 ++-- .../deploygate/sdk/truth/BundleSubject.java | 4 ++-- 15 files changed, 92 insertions(+), 65 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 0eb64db..2442e44 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -15,8 +15,8 @@ import android.text.TextUtils; import android.util.Log; -import com.deploygate.sdk.internal.VisibilityLifecycleCallbacks; import com.deploygate.sdk.internal.Logger; +import com.deploygate.sdk.internal.VisibilityLifecycleCallbacks; import com.deploygate.service.DeployGateEvent; import com.deploygate.service.IDeployGateSdkService; import com.deploygate.service.IDeployGateSdkServiceCallback; @@ -201,7 +201,7 @@ public void run() { public void onForeground( long elapsedRealtime, TimeUnit timeUnit - ) { + ) { Bundle extras = new Bundle(); extras.putLong(DeployGateEvent.EXTRA_VISIBILITY_EVENT_ELAPSED_REAL_TIME_IN_NANOS, timeUnit.toNanos(elapsedRealtime)); extras.putInt(DeployGateEvent.EXTRA_VISIBILITY_EVENT_TYPE, DeployGateEvent.VisibilityType.FOREGROUND); @@ -365,9 +365,12 @@ private void requestServiceInit(final boolean isBoot) { /** * Send an event to the client application * - * @param action to be sent - * @param extras to be sent - * @param allowPending Allow queueing events to send them after a service connection is established (since 4.6.0) + * @param action + * to be sent + * @param extras + * to be sent + * @param allowPending + * Allow queueing events to send them after a service connection is established (since 4.6.0) */ private void invokeAction( String action, diff --git a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java index 52f6e32..2302724 100644 --- a/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/ILogcatInstructionSerializer.java @@ -21,7 +21,8 @@ interface ILogcatInstructionSerializer { /** * Create and enqueue a request to start sending oneshot logcat * - * @param captureId this is nullable. Set to non-null if this logcat is for a capture. + * @param captureId + * this is nullable. Set to non-null if this logcat is for a capture. */ boolean requestOneshotLogcat(String captureId); diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java index eba54ad..ceaeae4 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java @@ -75,7 +75,7 @@ public void onStarted(String processId) { public void emit( String processId, ArrayList logcatLines, - String captureId + String captureId ) { ensureHandlerPrepared(); @@ -229,11 +229,17 @@ private boolean requestLogcat(String streamSessionKey) { } /** - * @param streamSessionKey nullable. sdk can not generate this key. - * @param captureId nullable. + * @param streamSessionKey + * nullable. sdk can not generate this key. + * @param captureId + * nullable. + * * @return true if new process has lauched */ - private boolean requestLogcat(String streamSessionKey, String captureId) { + private boolean requestLogcat( + String streamSessionKey, + String captureId + ) { ensureHandlerPrepared(); if (!isEnabled || logcatProcess == null) { diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java index 5ad94de..2d272ef 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatProcess.java @@ -26,9 +26,9 @@ interface Callback { void onStarted(String processId); void emit( - String processId, - ArrayList logcatLines, - String captureId + String processId, + ArrayList logcatLines, + String captureId ); void onFinished(String processId); @@ -59,8 +59,8 @@ void emit( * @return a pair of watcher ids (non-nulls). first is the previous watcher id, second is the new watcher id. */ Pair execute( - @Experimental String streamSessionKey, - String captureId + @Experimental String streamSessionKey, + String captureId ) { Pair ids; @@ -129,9 +129,9 @@ static class LogcatWatcher implements Runnable { private final AtomicInteger state; LogcatWatcher( - @Experimental String streamSessionKey, - String captureId, - Callback callback + @Experimental String streamSessionKey, + String captureId, + Callback callback ) { if (streamSessionKey != null && captureId != null) { throw new IllegalArgumentException("streaming and capture cannot be specified at once"); diff --git a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java index 7c47162..958620d 100644 --- a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java +++ b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java @@ -52,10 +52,14 @@ public static SendLogcatRequest createBeginning(String processId) { } /** - * @param pid a process id. non-null - * @param lines logcat contents if available. Zero value is an empty list. - * @param position a position of this request. non-null - * @param captureId the id of the capture. nullable + * @param pid + * a process id. non-null + * @param lines + * logcat contents if available. Zero value is an empty list. + * @param position + * a position of this request. non-null + * @param captureId + * the id of the capture. nullable */ private SendLogcatRequest( String pid, diff --git a/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java b/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java index a5c813f..3a21a56 100644 --- a/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java +++ b/sdk/src/main/java/com/deploygate/sdk/internal/VisibilityLifecycleCallbacks.java @@ -10,8 +10,15 @@ public final class VisibilityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { public interface OnVisibilityChangeListener { - void onForeground(long elapsedRealtime, TimeUnit timeUnit); - void onBackground(long elapsedRealtime, TimeUnit timeUnit); + void onForeground( + long elapsedRealtime, + TimeUnit timeUnit + ); + + void onBackground( + long elapsedRealtime, + TimeUnit timeUnit + ); } private int onResumeCount = 0; // this is manipulated from the single thread diff --git a/sdk/src/test/java/com/deploygate/sdk/CustomLogInstructionSerializerTest.java b/sdk/src/test/java/com/deploygate/sdk/CustomLogInstructionSerializerTest.java index 0c555fc..7bb8228 100644 --- a/sdk/src/test/java/com/deploygate/sdk/CustomLogInstructionSerializerTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/CustomLogInstructionSerializerTest.java @@ -1,5 +1,15 @@ package com.deploygate.sdk; +import static com.deploygate.sdk.mockito.BundleMatcher.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.robolectric.annotation.LooperMode.Mode.PAUSED; + import android.os.Bundle; import android.os.DeadObjectException; import android.os.RemoteException; @@ -26,16 +36,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.deploygate.sdk.mockito.BundleMatcher.eq; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.robolectric.annotation.LooperMode.Mode.PAUSED; - @RunWith(AndroidJUnit4.class) @LooperMode(PAUSED) public class CustomLogInstructionSerializerTest { diff --git a/sdk/src/test/java/com/deploygate/sdk/DeployGateClientTest.java b/sdk/src/test/java/com/deploygate/sdk/DeployGateClientTest.java index ccea880..fb4d016 100644 --- a/sdk/src/test/java/com/deploygate/sdk/DeployGateClientTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/DeployGateClientTest.java @@ -1,5 +1,7 @@ package com.deploygate.sdk; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + import android.app.Application; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -21,8 +23,6 @@ import org.robolectric.Shadows; import org.robolectric.shadows.ShadowPackageManager; -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; - @RunWith(AndroidJUnit4.class) public class DeployGateClientTest { @NonNull diff --git a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java index e2349f9..e5d4e15 100644 --- a/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/DeployGateInterfaceTest.java @@ -1,5 +1,7 @@ package com.deploygate.sdk; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + import android.app.Application; import androidx.annotation.NonNull; @@ -11,8 +13,6 @@ import org.junit.Test; import org.junit.runner.RunWith; -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; - /** * This test class will make sure all *public* interfaces are defined as expected diff --git a/sdk/src/test/java/com/deploygate/sdk/HostAppTest.java b/sdk/src/test/java/com/deploygate/sdk/HostAppTest.java index 4686656..afc8e39 100644 --- a/sdk/src/test/java/com/deploygate/sdk/HostAppTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/HostAppTest.java @@ -1,5 +1,7 @@ package com.deploygate.sdk; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + import android.content.Context; import androidx.annotation.NonNull; @@ -12,8 +14,6 @@ import org.junit.runner.RunWith; import org.robolectric.annotation.Config; -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; - @RunWith(AndroidJUnit4.class) public class HostAppTest { @NonNull diff --git a/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java b/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java index ca5669f..25a99d0 100644 --- a/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/LogcatInstructionSerializerTest.java @@ -1,5 +1,15 @@ package com.deploygate.sdk; +import static com.deploygate.sdk.mockito.BundleMatcher.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mockStatic; +import static org.robolectric.annotation.LooperMode.Mode.PAUSED; + import android.os.Bundle; import android.os.RemoteException; import android.os.TransactionTooLargeException; @@ -7,7 +17,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.deploygate.sdk.helper.FakeLogcat; -import com.deploygate.sdk.mockito.BundleMatcher; import com.deploygate.service.DeployGateEvent; import com.deploygate.service.FakeDeployGateClientService; import com.google.common.truth.Truth; @@ -29,17 +38,6 @@ import java.util.List; import java.util.Random; -import static com.deploygate.sdk.mockito.BundleMatcher.eq; -import static com.deploygate.sdk.mockito.BundleMatcher.eq; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mockStatic; -import static org.robolectric.annotation.LooperMode.Mode.PAUSED; - @RunWith(AndroidJUnit4.class) @LooperMode(PAUSED) public class LogcatInstructionSerializerTest { @@ -87,7 +85,7 @@ public void after() { instructionSerializer.halt(); } - for (Process process: processes) { + for (Process process : processes) { if (process.isAlive()) { process.destroy(); } diff --git a/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java b/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java index 11b8a2d..6b27d49 100644 --- a/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/LogcatProcessTest.java @@ -1,5 +1,8 @@ package com.deploygate.sdk; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.mockStatic; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -24,9 +27,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.mockStatic; - @RunWith(AndroidJUnit4.class) public class LogcatProcessTest { private FakeLogcat fakeLogcat; diff --git a/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java b/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java index c075d78..666d695 100644 --- a/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java @@ -1,5 +1,7 @@ package com.deploygate.sdk; +import static com.google.common.truth.Truth.assertAbout; + import android.os.Bundle; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -21,8 +23,6 @@ import java.util.Locale; import java.util.Objects; -import static com.google.common.truth.Truth.assertAbout; - @RunWith(AndroidJUnit4.class) public class SendLogcatRequestTest { @@ -155,7 +155,10 @@ private static Bundle createLogExtra( return bundle; } - private static ArrayList arrayListOf(int startInclusive, int count) { + private static ArrayList arrayListOf( + int startInclusive, + int count + ) { ArrayList list = new ArrayList<>(); for (int i = startInclusive, max = startInclusive + count; i < max; i++) { @@ -192,7 +195,12 @@ protected SendLogcatRequestSubject( this.actual = actual; } - public void isSameInBundle(String expectedKey, SendLogcatRequest.Position expectedPosition, List expectedLines, String captureId) { + public void isSameInBundle( + String expectedKey, + SendLogcatRequest.Position expectedPosition, + List expectedLines, + String captureId + ) { if (!actual.gid.equals(expectedKey)) { failWithActual(Fact.simpleFact(String.format(Locale.US, "%s is expected of %s", expectedKey, toString(actual)))); return; @@ -216,7 +224,7 @@ public void isSameInBundle(String expectedKey, SendLogcatRequest.Position expect } if (!Objects.equals(actual.captureId, captureId)) { - failWithActual(Fact.simpleFact(String.format(Locale.US, "%s is expected to be exactly included in %s",actual.captureId, captureId))); + failWithActual(Fact.simpleFact(String.format(Locale.US, "%s is expected to be exactly included in %s", actual.captureId, captureId))); } } diff --git a/sdk/src/test/java/com/deploygate/sdk/mockito/BundleMatcher.java b/sdk/src/test/java/com/deploygate/sdk/mockito/BundleMatcher.java index a9f8771..471df33 100644 --- a/sdk/src/test/java/com/deploygate/sdk/mockito/BundleMatcher.java +++ b/sdk/src/test/java/com/deploygate/sdk/mockito/BundleMatcher.java @@ -1,13 +1,13 @@ package com.deploygate.sdk.mockito; +import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress; + import android.os.Bundle; import com.deploygate.sdk.helper.Bundles; import org.mockito.ArgumentMatcher; -import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress; - public class BundleMatcher { public static Bundle eq(Bundle expected) { mockingProgress().getArgumentMatcherStorage().reportMatcher(new Equals(expected)); diff --git a/sdk/src/test/java/com/deploygate/sdk/truth/BundleSubject.java b/sdk/src/test/java/com/deploygate/sdk/truth/BundleSubject.java index 0e8402b..719483f 100644 --- a/sdk/src/test/java/com/deploygate/sdk/truth/BundleSubject.java +++ b/sdk/src/test/java/com/deploygate/sdk/truth/BundleSubject.java @@ -1,5 +1,7 @@ package com.deploygate.sdk.truth; +import static com.google.common.truth.Truth.assertAbout; + import android.os.Bundle; import com.deploygate.sdk.helper.Bundles; @@ -9,8 +11,6 @@ import java.util.Locale; -import static com.google.common.truth.Truth.assertAbout; - public class BundleSubject extends Subject { public static Factory bundles() { return new Factory() { From 169124e2232c175e1aaa1fbe1ca1d715a27f7fbb Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Thu, 17 Aug 2023 13:29:44 +0900 Subject: [PATCH 12/16] Update sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java Co-authored-by: satsukies --- .../java/com/deploygate/sdk/LogcatInstructionSerializer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java index ceaeae4..fd4dc45 100644 --- a/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java +++ b/sdk/src/main/java/com/deploygate/sdk/LogcatInstructionSerializer.java @@ -191,7 +191,6 @@ int sendSingleChunk( Bundle extras = request.toExtras(); try { - service.sendEvent(packageName, DeployGateEvent.ACTION_SEND_LOGCAT, extras); return SEND_LOGCAT_RESULT_SUCCESS; } catch (RemoteException e) { From 391e443a4509d5fc4e15c7eac34f6dc1b34eec63 Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Thu, 17 Aug 2023 13:34:43 +0900 Subject: [PATCH 13/16] feat: Add a compatibility enum for capture --- build.gradle | 2 +- sdk/src/main/java/com/deploygate/sdk/Compatibility.java | 4 +++- sdk/src/main/java/com/deploygate/sdk/DeployGate.java | 8 +++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 859bd99..c599ae2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. ext { - releaseVersion = '4.6.0-alpha01' + releaseVersion = '4.6.0-alpha02' } buildscript { diff --git a/sdk/src/main/java/com/deploygate/sdk/Compatibility.java b/sdk/src/main/java/com/deploygate/sdk/Compatibility.java index 723cc47..3710a2a 100644 --- a/sdk/src/main/java/com/deploygate/sdk/Compatibility.java +++ b/sdk/src/main/java/com/deploygate/sdk/Compatibility.java @@ -4,7 +4,9 @@ enum Compatibility { UPDATE_MESSAGE_OF_BUILD(1), SERIALIZED_EXCEPTION(1 << 1), LOGCAT_BUNDLE(1 << 2), - STREAMED_LOGCAT(1 << 3); + STREAMED_LOGCAT(1 << 3), + + DEVICE_CAPTURE(1 << 4); final int bitMask; diff --git a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java index 2442e44..9265236 100644 --- a/sdk/src/main/java/com/deploygate/sdk/DeployGate.java +++ b/sdk/src/main/java/com/deploygate/sdk/DeployGate.java @@ -100,7 +100,13 @@ public void onEvent( } else if (DeployGateEvent.ACTION_UPDATE_AVAILABLE.equals(action)) { onUpdateArrived(extras.getInt(DeployGateEvent.EXTRA_SERIAL), extras.getString(DeployGateEvent.EXTRA_VERSION_NAME), extras.getInt(DeployGateEvent.EXTRA_VERSION_CODE), extras.getString(DeployGateEvent.EXTRA_SERIAL_MESSAGE)); } else if (DeployGateEvent.ACTION_ONESHOT_LOGCAT.equals(action)) { - String captureId = extras.getString(DeployGateEvent.EXTRA_CAPTURE_ID); + String captureId = null; + + if (mDeployGateClient.isSupported(Compatibility.DEVICE_CAPTURE)) { + // still nullable + captureId = extras.getString(DeployGateEvent.EXTRA_CAPTURE_ID); + } + onOneshotLogcat(captureId); } else if (DeployGateEvent.ACTION_ENABLE_LOGCAT.equals(action)) { if (mDeployGateClient.isSupported(Compatibility.STREAMED_LOGCAT)) { From 4cae537d57b9316523dc19b5d2f74eaf201526a0 Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 22 Aug 2023 12:34:26 +0900 Subject: [PATCH 14/16] feat: add feature_flags to communicate with the client app --- sdk.build.gradle | 33 ++++++++++++++++++- sdk/src/main/AndroidManifest.xml | 5 +++ .../com/deploygate/sdk/Compatibility.java | 15 ++++++--- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/sdk.build.gradle b/sdk.build.gradle index 6dab261..5a31a42 100644 --- a/sdk.build.gradle +++ b/sdk.build.gradle @@ -11,7 +11,34 @@ android { targetSdkVersion 33 versionCode 1 versionName project.version - manifestPlaceholders = [sdkVersion:"4"] + + // A map of name to isSupporting + def features = [ + UPDATE_MESSAGE_OF_BUILD: true, + SERIALIZED_EXCEPTION: true, + LOGCAT_BUNDLE: true, + STREAMED_LOGCAT: true, + DEVICE_CAPTURE: true, + ] + + if (!(features instanceof LinkedHashMap)) { + throw new IllegalAccessException("The key order may not be kept") + } + + int flags = 0 + + features.keySet().eachWithIndex { String key, int i -> + buildConfigField("int", key, "1 << $i") + + if (features[key]) { + flags |= 1 << i + } + } + + manifestPlaceholders += [ + featureFlags: flags, + sdkVersion: "4" + ] } buildTypes { @@ -37,6 +64,10 @@ android { jvmArgs "-Xmx1g" } } + + buildFeatures { + buildConfig = true + } } dependencies { diff --git a/sdk/src/main/AndroidManifest.xml b/sdk/src/main/AndroidManifest.xml index 5491859..914a0b1 100644 --- a/sdk/src/main/AndroidManifest.xml +++ b/sdk/src/main/AndroidManifest.xml @@ -13,6 +13,11 @@ android:name="com.deploygate.sdk.version" android:value="${sdkVersion}" /> + + diff --git a/sdk/src/main/java/com/deploygate/sdk/Compatibility.java b/sdk/src/main/java/com/deploygate/sdk/Compatibility.java index 3710a2a..f60c075 100644 --- a/sdk/src/main/java/com/deploygate/sdk/Compatibility.java +++ b/sdk/src/main/java/com/deploygate/sdk/Compatibility.java @@ -1,12 +1,17 @@ package com.deploygate.sdk; +/** + * A metadata content to represent feature compatibilities of the client app. + * + * NOTE: Do not remove any enum entry even if it's completely removed from the client app. + */ enum Compatibility { - UPDATE_MESSAGE_OF_BUILD(1), - SERIALIZED_EXCEPTION(1 << 1), - LOGCAT_BUNDLE(1 << 2), - STREAMED_LOGCAT(1 << 3), + UPDATE_MESSAGE_OF_BUILD(BuildConfig.UPDATE_MESSAGE_OF_BUILD), + SERIALIZED_EXCEPTION(BuildConfig.SERIALIZED_EXCEPTION), + LOGCAT_BUNDLE(BuildConfig.LOGCAT_BUNDLE), + STREAMED_LOGCAT(BuildConfig.STREAMED_LOGCAT), - DEVICE_CAPTURE(1 << 4); + DEVICE_CAPTURE(BuildConfig.DEVICE_CAPTURE); final int bitMask; From 724c234319a11e675196d5125d7c035f0b42f266 Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Tue, 22 Aug 2023 12:34:53 +0900 Subject: [PATCH 15/16] Bump up alpha03 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c599ae2..50fcc62 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. ext { - releaseVersion = '4.6.0-alpha02' + releaseVersion = '4.6.0-alpha03' } buildscript { From e906a15919a54ce6c810ffadb87c700739482693 Mon Sep 17 00:00:00 2001 From: Jumpei Matsuda Date: Fri, 8 Sep 2023 14:40:34 +0900 Subject: [PATCH 16/16] fix: Don't send null extra to the client app cuz it's meaningless --- sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java | 4 +++- .../test/java/com/deploygate/sdk/SendLogcatRequestTest.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java index 958620d..f0eed77 100644 --- a/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java +++ b/sdk/src/main/java/com/deploygate/sdk/SendLogcatRequest.java @@ -111,6 +111,8 @@ List splitInto(int count) { void applyValues(Bundle extras) { extras.putStringArrayList(DeployGateEvent.EXTRA_LOG, lines); extras.putString(DeployGateEvent.EXTRA_BUNDLE_POSITION, position.label()); - extras.putString(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); + if (captureId != null) { + extras.putString(DeployGateEvent.EXTRA_CAPTURE_ID, captureId); + } } } diff --git a/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java b/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java index 666d695..3231e0a 100644 --- a/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java +++ b/sdk/src/test/java/com/deploygate/sdk/SendLogcatRequestTest.java @@ -151,7 +151,9 @@ private static Bundle createLogExtra( bundle.putString("e.cid", cid); bundle.putStringArrayList("log", lines); bundle.putString("e.bundle-position", positionLabel); - bundle.putString("e.capture-id", captureId); + if (captureId != null) { + bundle.putString("e.capture-id", captureId); + } return bundle; }