Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -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-alpha03'
}

buildscript {
Expand Down
5 changes: 5 additions & 0 deletions jitpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
jdk:
- openjdk17
before_install:
- sdk install java 17.0.8-tem
- sdk use java 17.0.8-tem
33 changes: 32 additions & 1 deletion sdk.build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -37,6 +64,10 @@ android {
jvmArgs "-Xmx1g"
}
}

buildFeatures {
buildConfig = true
}
}

dependencies {
Expand Down
5 changes: 5 additions & 0 deletions sdk/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
android:name="com.deploygate.sdk.version"
android:value="${sdkVersion}"
/>

<meta-data
android:name="com.deploygate.sdk.feature_flags"
android:value="${featureFlags}"
/>
</application>
<queries>
<package android:name="com.deploygate" />
Expand Down
15 changes: 11 additions & 4 deletions sdk/src/main/java/com/deploygate/sdk/Compatibility.java
Original file line number Diff line number Diff line change
@@ -1,10 +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(BuildConfig.DEVICE_CAPTURE);

final int bitMask;

Expand Down
100 changes: 91 additions & 9 deletions sdk/src/main/java/com/deploygate/sdk/DeployGate.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@
import android.util.Log;

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;

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;

/**
* This is DeployGate SDK library. Import this library to the application
Expand All @@ -42,6 +47,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;

Expand All @@ -52,6 +58,7 @@ public class DeployGate {
private final ILogcatInstructionSerializer mLogcatInstructionSerializer;
private final CustomLogInstructionSerializer mCustomLogInstructionSerializer;
private final HashSet<DeployGateCallback> mCallbacks;
private final HashMap<String, Bundle> mPendingEvents;
private final String mExpectedAuthor;
private String mAuthor;

Expand Down Expand Up @@ -85,12 +92,22 @@ public void onEvent(
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)) {
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 = 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)) {
String sessionKey = extras.getString(DeployGateEvent.EXTRA_LOGCAT_STREAM_SESSION_KEY);
Expand All @@ -110,6 +127,8 @@ public void onEvent(
} else {
Logger.w("streamed logcat is not supported");
}
} else {
Logger.w("%s is not supported by this sdk version", action);
}
}

Expand Down Expand Up @@ -153,6 +172,9 @@ public void run() {
mLogcatInstructionSerializer.connect(mRemoteService);

mInitializedLatch.countDown();

// to release a lock as soon as possible.
flushPendingEvents();
}

private void onUpdateArrived(
Expand All @@ -178,12 +200,38 @@ public void run() {
}
};

@SuppressWarnings("FieldCanBeLocal")
private final VisibilityLifecycleCallbacks.OnVisibilityChangeListener mOnVisibilityChangeListener = new VisibilityLifecycleCallbacks.OnVisibilityChangeListener() {

@Override
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);
invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras, true);
}

@Override
public void onBackground(
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.BACKGROUND);
invokeAction(DeployGateEvent.ACTION_VISIBILITY_EVENT, extras, true);
}
};

private void requestOneshotLogcat() {
onOneshotLogcat();
onOneshotLogcat(null);
}

private void onOneshotLogcat() {
mLogcatInstructionSerializer.requestOneshotLogcat();
private void onOneshotLogcat(String captureId) {
mLogcatInstructionSerializer.requestOneshotLogcat(captureId);
}

private void onEnableStreamedLogcat(String streamSessionKey) {
Expand Down Expand Up @@ -229,7 +277,8 @@ private DeployGate(
mHandler = new Handler();
mLogcatInstructionSerializer = mHostApp.canUseLogcat ? new LogcatInstructionSerializer(mHostApp.packageName) : ILogcatInstructionSerializer.NULL_INSTANCE;
mCustomLogInstructionSerializer = new CustomLogInstructionSerializer(mHostApp.packageName, customLogConfiguration);
mCallbacks = new HashSet<DeployGateCallback>();
mCallbacks = new HashSet<>();
mPendingEvents = new HashMap<>();
mExpectedAuthor = author;

prepareBroadcastReceiver();
Expand All @@ -239,6 +288,7 @@ private DeployGate(
}

mInitializedLatch = new CountDownLatch(1);
((Application) applicationContext).registerActivityLifecycleCallbacks(new VisibilityLifecycleCallbacks(mOnVisibilityChangeListener));
initService(true);
}

Expand Down Expand Up @@ -318,11 +368,30 @@ 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(
String action,
Bundle extras
Bundle extras,
boolean allowPending
) {
extras = extras != null ? extras : new Bundle();

if (mRemoteService == null) {
if (allowPending) {
synchronized (sPendingEventLock) {
mPendingEvents.put(action, extras);
}
}

return;
}
try {
Expand All @@ -332,6 +401,19 @@ private void invokeAction(
}
}

private void flushPendingEvents() {
// cannot re-enqueue events for now
synchronized (sPendingEventLock) {
Iterator<Map.Entry<String, Bundle>> iterator = mPendingEvents.entrySet().iterator();

while (iterator.hasNext()) {
Map.Entry<String, Bundle> entry = iterator.next();
invokeAction(entry.getKey(), entry.getValue(), false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current buffer mechanism is relaxed. Enough.

iterator.remove();
}
}
}

/**
* Clear the initiated DeployGate instance.
* <p>
Expand Down Expand Up @@ -1191,7 +1273,7 @@ public static void installUpdate() {
return;
}

sInstance.invokeAction(DeployGateEvent.ACTION_INSTALL_UPDATE, null);
sInstance.invokeAction(DeployGateEvent.ACTION_INSTALL_UPDATE, null, false);
}

/**
Expand All @@ -1207,7 +1289,7 @@ public static void openComments() {
return;
}

sInstance.invokeAction(DeployGateEvent.ACTION_OPEN_COMMENTS, null);
sInstance.invokeAction(DeployGateEvent.ACTION_OPEN_COMMENTS, null, false);
}

/**
Expand Down Expand Up @@ -1240,7 +1322,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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ 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();
boolean requestOneshotLogcat(String captureId);

/**
* Create and enqueue a request to start sending streamed logcat
Expand Down Expand Up @@ -56,8 +59,8 @@ public void disconnect() {
}

@Override
public boolean requestOneshotLogcat() {
Logger.d("Logcat (no-op): requestOneshotLogcat");
public boolean requestOneshotLogcat(String captureId) {
Logger.d("Logcat (no-op): requestOneshotLogcat(%s)", captureId != null ? captureId : "null");
return false;
}

Expand Down
Loading