Skip to content

Commit

Permalink
tv-casting-app: Implemented getDeviceProxy, used android chip IM
Browse files Browse the repository at this point in the history
  • Loading branch information
sharadb-amazon committed Apr 16, 2024
1 parent fd72eae commit e5da6be
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ public class GlobalCastingConstants {
public static final int SetupPasscode = 20202021;
public static final int Discriminator = 0xF00;
public static final boolean ChipCastingSimplified =
false; // set this flag to true to demo simplified casting APIs
true; // set this flag to true to demo simplified casting APIs
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import chip.devicecontroller.ChipClusters;
import com.R;
import com.matter.casting.core.CastingPlayer;
import com.matter.casting.core.Endpoint;
import java.util.List;
import java.util.Optional;

/** A {@link Fragment} to send Content Launcher LaunchURL command from the TV Casting App. */
public class ContentLauncherLaunchURLExampleFragment extends Fragment {
Expand Down Expand Up @@ -73,7 +75,30 @@ public View onCreateView(
return;
}

// TODO: add command invocation API call
// TODO: complete command invocation API implementation
ChipClusters.ContentLauncherCluster cluster =
endpoint.getCluster(ChipClusters.ContentLauncherCluster.class);
if (cluster == null) {
Log.e(
TAG,
"Could not get ContentLauncherCluster for endpoint with ID: " + endpoint.getId());
}

cluster.launchURL(
new ChipClusters.ContentLauncherCluster.LauncherResponseCallback() {
@Override
public void onSuccess(Integer status, Optional<String> data) {
Log.d(TAG, "Content launcher success " + status + data);
}

@Override
public void onError(Exception error) {
Log.e(TAG, "Content launcher failure " + error);
}
},
"my test url",
Optional.of("my display str"),
Optional.empty());
};
return inflater.inflate(R.layout.fragment_content_launcher, container, false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@

package com.matter.casting.core;

import chip.devicecontroller.ChipClusters;
import com.matter.casting.support.DeviceTypeStruct;
import java.util.List;

/** This represents an Endpoint on a CastingPlayer e.g. a Speaker or a Matter Content App */
public interface Endpoint {
int getId();

Expand All @@ -29,5 +31,9 @@ public interface Endpoint {

List<DeviceTypeStruct> getDeviceTypeList();

/** Get an instance of a cluster based on its Class */
<T extends ChipClusters.BaseChipCluster> T getCluster(Class<T> clusterClass);

/** Get the CastingPlayer that this Endpoint is a part of. */
CastingPlayer getCastingPlayer();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,23 @@
*/
package com.matter.casting.core;

import android.util.Log;
import chip.devicecontroller.ChipClusters;
import com.matter.casting.support.DeviceTypeStruct;
import com.matter.casting.support.MatterCallback;
import com.matter.casting.support.MatterError;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class MatterEndpoint implements Endpoint {
private static final String TAG = MatterEndpoint.class.getSimpleName();
private static final long MAX_WAIT_FOR_DEVICE_PROXY_SEC = 5000;
protected long _cppEndpoint;

@Override
Expand All @@ -36,6 +47,27 @@ public class MatterEndpoint implements Endpoint {
@Override
public native List<DeviceTypeStruct> getDeviceTypeList();

@Override
public <T extends ChipClusters.BaseChipCluster> T getCluster(Class<T> clusterClass) {
try {
Constructor<T> constructor = clusterClass.getDeclaredConstructor(long.class, int.class);
Long deviceProxy = getDeviceProxy();
if (deviceProxy == null) {
Log.e(TAG, "Could not get DeviceProxy while constructing cluster object");
return null;
}
return constructor.newInstance(deviceProxy, getId());
} catch (InstantiationException
| IllegalAccessException
| InvocationTargetException
| NoSuchMethodException e) {
Log.e(
TAG,
"Could not create cluster object for " + clusterClass.getSimpleName() + " exc: " + e);
return null;
}
}

@Override
public native CastingPlayer getCastingPlayer();

Expand All @@ -56,4 +88,32 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(getId());
}

private Long getDeviceProxy() {
CompletableFuture<Long> deviceProxyFuture = new CompletableFuture<>();
getDeviceProxy(
new MatterCallback<Long>() {
@Override
public void handle(Long deviceProxy) {
deviceProxyFuture.complete(deviceProxy);
}
},
new MatterCallback<MatterError>() {
@Override
public void handle(MatterError response) {
deviceProxyFuture.completeExceptionally(
new RuntimeException("Failed on getDeviceProxy: " + response));
}
});

try {
return deviceProxyFuture.get(MAX_WAIT_FOR_DEVICE_PROXY_MS, TimeUnit.MILLISECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
Log.e(TAG, "Exception while waiting on getDeviceProxy future: " + e);
return null;
}
}

protected native void getDeviceProxy(
MatterCallback<Long> successCallback, MatterCallback<MatterError> failureCallback);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#include "MatterEndpoint-JNI.h"

#include "../JNIDACProvider.h"
#include "../support/Converters-JNI.h"
#include "../support/MatterCallback-JNI.h"
#include "../support/RotatingDeviceIdUniqueIdProvider-JNI.h"
#include "clusters/Clusters.h" // from tv-casting-common
Expand Down Expand Up @@ -86,6 +85,30 @@ JNI_METHOD(jobject, getCastingPlayer)
return support::convertCastingPlayerFromCppToJava(std::shared_ptr<CastingPlayer>(endpoint->GetCastingPlayer()));
}

JNI_METHOD(void, getDeviceProxy)
(JNIEnv * env, jobject thiz, jobject jSuccessCallback, jobject jFailureCallback)
{
chip::DeviceLayer::StackLock lock;
ChipLogProgress(AppServer, "MatterEndpoint-JNI::getDeviceProxy() called");
Endpoint * endpoint = support::convertEndpointFromJavaToCpp(thiz);
VerifyOrReturn(endpoint != nullptr, ChipLogError(AppServer, "MatterEndpoint-JNI::getDeviceProxy() endpoint == nullptr"));

ReturnOnFailure(MatterEndpointJNIMgr().mGetDeviceProxySuccessHandler.SetUp(env, jSuccessCallback));
ReturnOnFailure(MatterEndpointJNIMgr().mGetDeviceProxyFailureHandler.SetUp(env, jFailureCallback));

endpoint->GetCastingPlayer()->FindOrEstablishSession(
nullptr,
[](void * context, chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle) {
ChipLogProgress(AppServer, "MatterEndpointJNI FindOrEstablishSession success");
OperationalDeviceProxy * device = new OperationalDeviceProxy(&exchangeMgr, sessionHandle); // TODO: delete *device
MatterEndpointJNIMgr().mGetDeviceProxySuccessHandler.Handle(device);
},
[](void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR error) {
ChipLogError(AppServer, "MatterEndpointJNI FindOrEstablishSession failure %" CHIP_ERROR_FORMAT, error.Format());
MatterEndpointJNIMgr().mGetDeviceProxyFailureHandler.Handle(error);
});
}

}; // namespace core
}; // namespace casting
}; // namespace matter
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@

#pragma once

#include "../support/Converters-JNI.h"
#include "../support/MatterCallback-JNI.h"
#include "core/Endpoint.h" // from tv-casting-common

#include <app/DeviceProxy.h>
#include <jni.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
Expand All @@ -30,6 +33,15 @@ namespace core {

class MatterEndpointJNI
{
public:
MatterEndpointJNI() :
mGetDeviceProxySuccessHandler([](chip::DeviceProxy * device) -> jobject {
return support::convertLongFromCppToJava(reinterpret_cast<jlong>(device));
})
{}
support::MatterCallbackJNI<chip::DeviceProxy *> mGetDeviceProxySuccessHandler;
support::MatterFailureCallbackJNI mGetDeviceProxyFailureHandler;

private:
friend MatterEndpointJNI & MatterEndpointJNIMgr();
static MatterEndpointJNI sInstance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ namespace support {

using namespace chip;

jobject convertLongFromCppToJava(jlong value)
{
ChipLogProgress(AppServer, "convertLongFromCppToJava called");
JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturnValue(env != nullptr, nullptr, ChipLogError(AppServer, "Could not get JNIEnv for current thread"));

jclass responseTypeClass = env->FindClass("java/lang/Long");
if (responseTypeClass == nullptr)
{
ChipLogError(AppServer, "ConvertToJObject: Class for Response Type not found!");
env->ExceptionClear();
return nullptr;
}

jmethodID constructor = env->GetMethodID(responseTypeClass, "<init>", "(J)V");
if (constructor == nullptr)
{
ChipLogError(AppServer, "Failed to access Long constructor");
env->ExceptionClear();
return nullptr;
}
return env->NewObject(responseTypeClass, constructor, value);
}

jobject convertMatterErrorFromCppToJava(CHIP_ERROR inErr)
{
ChipLogProgress(AppServer, "convertMatterErrorFromCppToJava() called");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ namespace matter {
namespace casting {
namespace support {

jobject convertLongFromCppToJava(jlong value);

jobject convertMatterErrorFromCppToJava(CHIP_ERROR inErr);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class MatterCallbackJNI
ChipLogProgress(AppServer, "MatterCallbackJNI::SetUp called");
VerifyOrReturnError(env != nullptr, CHIP_JNI_ERROR_NO_ENV, ChipLogError(AppServer, "JNIEnv was null!"));

mCallbackObject.Reset();
ReturnErrorOnFailure(mCallbackObject.Init(inCallback));

jclass mClazz = env->GetObjectClass(mCallbackObject.ObjectRef());
Expand All @@ -53,7 +54,7 @@ class MatterCallbackJNI
VerifyOrReturnError(mSuperClazz != nullptr, CHIP_JNI_ERROR_TYPE_NOT_FOUND,
ChipLogError(AppServer, "Failed to get callback's parent's Java class"));

mMethod = env->GetMethodID(mClazz, "handleInternal", mMethodSignature);
mMethod = env->GetMethodID(mSuperClazz, "handleInternal", mMethodSignature);
VerifyOrReturnError(
mMethod != nullptr, CHIP_JNI_ERROR_METHOD_NOT_FOUND,
ChipLogError(AppServer, "Failed to access 'handleInternal' method with signature %s", mMethodSignature));
Expand Down

0 comments on commit e5da6be

Please sign in to comment.