Skip to content
Closed
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
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ android {
androidResources.generateLocaleConfig = true

buildFeatures {
aidl = true
compose = true
prefab = true
buildConfig = true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ru.playsoftware.j2meloader.memory;

import android.os.Process;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class MemoryTargetProbeTest {
private static final int MANAGED_PROBE_A = 0x5A17C0DE;
private static final int MANAGED_PROBE_B = 0x31C0FFEE;
private static volatile int managedProbe;

@Test
public void thoroughProbeReturnsAlignedCompleteResidentRuns() {
int pageSize = NativeMemoryTarget.pageSize();
assertTrue(pageSize > 0);

long[] runs = NativeMemoryTarget.collectResidentRuns(
MemoryEngineContract.SCOPE_JAVA_THOROUGH, 4096);
assertNotNull(runs);
assertTrue(MemoryEngineContract.isCompleteRunList(runs));
for (int index = 2; index < runs.length; index += 2) {
assertTrue(runs[index] > 0L);
assertTrue(runs[index + 1] > runs[index]);
assertEquals(0L, runs[index] % pageSize);
assertEquals(0L, runs[index + 1] % pageSize);
if (index > 2) {
assertTrue(runs[index] >= runs[index - 1]);
}
}
}

@Test
public void readCapabilityProbeVerifiesExpectedBits() {
long[] probe = NativeMemoryTarget.readProbe();
assertNotNull(probe);
assertEquals(2, probe.length);
assertTrue(NativeMemoryEngine.canReadTarget(Process.myPid(), probe[0], probe[1]));
assertTrue(!NativeMemoryEngine.canReadTarget(Process.myPid(), probe[0], probe[1] ^ 1L));
}

@Test
public void nativeEngineFindsAndRefinesManagedArtValue() {
int pageSize = NativeMemoryTarget.pageSize();
long[] runs = NativeMemoryTarget.collectResidentRuns(
MemoryEngineContract.SCOPE_JAVA_FAST, 4096);
assertNotNull(runs);
assertTrue(MemoryEngineContract.isCompleteRunList(runs));

long token = 0x4A4C4D454D544553L;
managedProbe = MANAGED_PROBE_A;
try {
assertEquals(MemoryEngineContract.RESULT_OK,
NativeMemoryEngine.configureTarget(
Process.myPid(), pageSize, token, runs));
assertEquals(MemoryEngineContract.RESULT_OK,
NativeMemoryEngine.startKnown(
MemoryEngineContract.TYPE_INT,
MemoryEngineContract.PREDICATE_EQUAL,
Integer.toString(MANAGED_PROBE_A), ""));
assertTrue("managed ART probe was not inside the selected Java ranges",
NativeMemoryEngine.resultCount() > 0L);

managedProbe = MANAGED_PROBE_B;
assertEquals(MemoryEngineContract.RESULT_OK,
NativeMemoryEngine.refineKnown(
MemoryEngineContract.PREDICATE_EQUAL,
Integer.toString(MANAGED_PROBE_B), ""));
assertTrue("managed ART probe did not survive direct refine",
NativeMemoryEngine.resultCount() > 0L);
} finally {
managedProbe = 0;
NativeMemoryEngine.clear();
}
}

@Test
public void runtimeTokenCannotBeClosedByAnOlderOwner() {
long[] endedToken = {0L};
MemoryRuntimeSession.Listener listener = token -> endedToken[0] = token;
MemoryRuntimeSession.addListener(listener);
try {
long token = MemoryRuntimeSession.start();
assertTrue(token != 0L);
MemoryRuntimeSession.close(token ^ 1L);
assertTrue(MemoryRuntimeSession.isActive(token));
assertEquals(0L, endedToken[0]);
MemoryRuntimeSession.close(token);
assertEquals(0L, MemoryRuntimeSession.currentToken());
assertEquals(token, endedToken[0]);
} finally {
MemoryRuntimeSession.close(MemoryRuntimeSession.currentToken());
MemoryRuntimeSession.removeListener(listener);
}
}
}
8 changes: 8 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="j2me_midlet_runtime" />
</service>
<service
android:name=".memory.MemoryTargetBridgeService"
android:exported="false"
android:process=":midlet" />
<service
android:name=".memory.MemoryEngineService"
android:exported="false"
android:process=":memory_engine" />
<activity android:name=".settings.SettingsActivity"
android:exported="true">
<intent-filter>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
*/

package ru.playsoftware.j2meloader.memory;

/** Publishes completion of an asynchronous Memory Editor engine operation. */
oneway interface IMemoryEngineCallback {
void onOperationFinished(long operationId, int resultCode, long resultCount, String message);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
*/

package ru.playsoftware.j2meloader.memory;

import android.os.Bundle;
import ru.playsoftware.j2meloader.memory.IMemoryEngineCallback;

/** Logical API; result addresses are informational and no operation accepts a raw destination. */
interface IMemoryEngineService {
Bundle getCapabilities();
void registerCallback(IMemoryEngineCallback callback);
void unregisterCallback(IMemoryEngineCallback callback);

long startKnownSearch(long runtimeToken, int scope, int valueType, int predicate,
String firstValue, String secondValue);
long startUnknownSearch(long runtimeToken, int scope, int valueType);
long refineKnown(long runtimeToken, int predicate, String firstValue, String secondValue);
long refineRelative(long runtimeToken, int predicate, int compareTarget,
String firstValue, String secondValue);
long undoSearch(long runtimeToken);

long getResultCount(long runtimeToken);
long[] getResultPage(long runtimeToken, int offset, int limit);
void clearSearch(long runtimeToken);
void cancelOperation(long runtimeToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
*/

package ru.playsoftware.j2meloader.memory;

import ru.playsoftware.j2meloader.memory.IMemoryTargetCallback;

/** Thin target-process bridge. Scanning and candidate ownership remain in :memory_engine. */
interface IMemoryTargetBridge {
void registerTargetCallback(IMemoryTargetCallback callback);
void unregisterTargetCallback(IMemoryTargetCallback callback);
long getRuntimeToken();
int getTargetPid();
int getPageSize();
/** Returns [address, expectedBits] for a target-owned read-only capability probe. */
long[] getReadProbe(long runtimeToken);

/**
* Returns [runCount, truncated, start0, end0, ...]. Only resident readable/writable runs
* selected by the requested scope are returned.
*/
long[] getResidentRuns(long runtimeToken, int scope, int maxRuns);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
*/

package ru.playsoftware.j2meloader.memory;

/** Notifies the engine that an address namespace is no longer valid. */
oneway interface IMemoryTargetCallback {
void onRuntimeEnded(long runtimeToken);
}
2 changes: 1 addition & 1 deletion app/src/main/cpp/Android.mk
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include $(call all-subdir-makefiles)
include $(call all-subdir-makefiles)
17 changes: 17 additions & 0 deletions app/src/main/cpp/memory/Android.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := jlmem_target
LOCAL_SRC_FILES := target_probe.cpp
LOCAL_CPPFLAGS := -std=c++17 -Wall -Wextra -Werror
LOCAL_CPP_FEATURES := exceptions
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := jlmem
LOCAL_SRC_FILES := memory_engine.cpp
LOCAL_CPPFLAGS := -std=c++17 -Wall -Wextra -Werror
LOCAL_CPP_FEATURES := exceptions
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
Loading