Skip to content

Commit

Permalink
Enable command passing from clients to content apps (#19506)
Browse files Browse the repository at this point in the history
  • Loading branch information
amitnj authored Jun 14, 2022
1 parent efc2109 commit 7690b96
Show file tree
Hide file tree
Showing 18 changed files with 270 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ public class MatterIntentConstants {

public static final String EXTRA_DIRECTIVE_RESPONSE_PENDING_INTENT =
"EXTRA_DIRECTIVE_RESPONSE_PENDING_INTENT";

public static final String EXTRA_COMMAND_ID = "EXTRA_COMMAND_ID";
public static final String EXTRA_CLUSTER_ID = "EXTRA_CLUSTER_ID";
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
android:theme="@style/Theme.ContentApp">
<meta-data android:name="com.matter.tv.app.api.clusters" android:resource="@raw/static_matter_clusters" />
<meta-data android:name="com.matter.tv.app.api.vendor_name" android:value="CSA" />
<meta-data android:name="com.matter.tv.app.api.vendor_id" android:value="1234" />
<meta-data android:name="com.matter.tv.app.api.vendor_id" android:value="65521" />
<meta-data android:name="com.matter.tv.app.api.product_id" android:value="5678" />
<activity
android:name="com.example.contentapp.MainActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ public void onReceive(Context context, Intent intent) {
case MatterIntentConstants.ACTION_MATTER_COMMAND:
byte[] commandPayload =
intent.getByteArrayExtra(MatterIntentConstants.EXTRA_COMMAND_PAYLOAD);
int commandId = intent.getIntExtra(MatterIntentConstants.EXTRA_COMMAND_ID, -1);
int clusterId = intent.getIntExtra(MatterIntentConstants.EXTRA_CLUSTER_ID, -1);
Log.d(
TAG,
new StringBuilder()
.append("Received matter command: ")
.append(intent.getAction())
.append(". Payload : ")
.append("Received matter command ")
.append(commandId)
.append(" on cluster ")
.append(clusterId)
.append(" with payload : ")
.append(new String(commandPayload))
.toString());

Expand All @@ -38,7 +42,10 @@ public void onReceive(Context context, Intent intent) {
if (pendingIntent != null) {
final Intent responseIntent =
new Intent()
.putExtra(MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD, "Success".getBytes());
.putExtra(
MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD,
"{\"value\":{\"0\":1, \"1\":\"custom response from content app\"}}"
.getBytes());
try {
pendingIntent.send(context, 0, responseIntent);
} catch (final PendingIntent.CanceledException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,23 @@
"clusters": [
{
"identifier": 1289,
"features": ["NV", "LK", "NK"]
"features": [
"NV",
"LK",
"NK"
]
},
{
"identifier": 1290,
"features": ["CS"],
"features": [
"CS"
]
},
{
"identifier": 1286,
"features": ["AS"],
"features": [
"AS"
]
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ public ContentAppEndpointManagerImpl(Context context) {
this.context = context;
}

public String sendCommand(int endpointId, String commandPayload) {
public String sendCommand(int endpointId, int clusterId, int commandId, String commandPayload) {
Log.d(TAG, "Received a command for endpointId " + endpointId + ". Message " + commandPayload);
for (ContentApp app :
ContentAppDiscoveryService.getReceiverInstance().getDiscoveredContentApps().values()) {
if (app.getEndpointId() == endpointId) {
Log.d(
TAG, "Sending a command for endpointId " + endpointId + ". Message " + commandPayload);
ContentAppAgentService.sendCommand(context, app.getAppName(), commandPayload);
return ContentAppAgentService.sendCommand(
context, app.getAppName(), clusterId, commandId, commandPayload);
}
}
return "Success";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import androidx.annotation.Nullable;
import com.matter.tv.app.api.IMatterAppAgent;
import com.matter.tv.app.api.MatterIntentConstants;
import java.util.concurrent.TimeUnit;

public class ContentAppAgentService extends Service {

Expand All @@ -20,6 +21,8 @@ public class ContentAppAgentService extends Service {
public static final String EXTRA_RESPONSE_RECEIVING_PACKAGE = "EXTRA_RESPONSE_RECEIVING_PACKAGE";
public static final String EXTRA_RESPONSE_ID = "EXTRA_RESPONSE_ID";

private static ResponseRegistry responseRegistry = new ResponseRegistry();

private final IBinder appAgentBinder =
new IMatterAppAgent.Stub() {
@Override
Expand Down Expand Up @@ -51,23 +54,34 @@ public IBinder onBind(final Intent intent) {
return null;
}

public static void sendCommand(Context context, String packageName, String payload) {
public static String sendCommand(
Context context, String packageName, int clusterId, int commandId, String payload) {
Intent in = new Intent(MatterIntentConstants.ACTION_MATTER_COMMAND);
Bundle extras = new Bundle();
extras.putByteArray(MatterIntentConstants.EXTRA_COMMAND_PAYLOAD, payload.getBytes());
extras.putInt(MatterIntentConstants.EXTRA_COMMAND_ID, commandId);
extras.putInt(MatterIntentConstants.EXTRA_CLUSTER_ID, clusterId);
in.putExtras(extras);
in.setPackage(packageName);
int flags = Intent.FLAG_INCLUDE_STOPPED_PACKAGES;
flags |= Intent.FLAG_RECEIVER_FOREGROUND;
in.setFlags(flags);
int messageId = responseRegistry.getNextMessageCounter();
in.putExtra(
MatterIntentConstants.EXTRA_DIRECTIVE_RESPONSE_PENDING_INTENT,
getPendingIntentForResponse(context, packageName, "0"));
getPendingIntentForResponse(context, packageName, messageId));
context.sendBroadcast(in);
responseRegistry.waitForMessage(messageId, 10, TimeUnit.SECONDS);
String response = responseRegistry.readAndRemoveResponse(messageId);
if (response == null) {
response = "";
}
Log.d(TAG, "Response " + response + " being returned for message " + messageId);
return response;
}

private static PendingIntent getPendingIntentForResponse(
final Context context, final String targetPackage, final String responseId) {
final Context context, final String targetPackage, final int responseId) {
Intent ackBackIntent = new Intent(ACTION_MATTER_RESPONSE);
ackBackIntent.setClass(context, ContentAppAgentService.class);
ackBackIntent.putExtra(EXTRA_RESPONSE_RECEIVING_PACKAGE, targetPackage);
Expand All @@ -78,16 +92,13 @@ private static PendingIntent getPendingIntentForResponse(

@Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
Log.d(TAG, "onStartCommand");

if (intent != null && ACTION_MATTER_RESPONSE.equals(intent.getAction())) {
Log.d(
TAG,
"Command response "
+ new String(intent.getByteArrayExtra(MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD)));
// Send the response back to the client.
String response =
new String(intent.getByteArrayExtra(MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD));
int messageId = intent.getIntExtra(EXTRA_RESPONSE_ID, Integer.MAX_VALUE);
Log.d(TAG, "Response " + response + " received for message " + messageId);
responseRegistry.receivedMessageResponse(messageId, response);
}

return START_NOT_STICKY;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public void onCommissioningComplete() {
chipAppServer = new ChipAppServer();
chipAppServer.startApp();

mTvApp.postServerInit();
mTvApp.postServerInit(new ContentAppEndpointManagerImpl(context));
}

public void restart() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.matter.tv.server.service;

import android.util.Log;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ResponseRegistry {

private static final String TAG = "ResponseRegistry";

private AtomicInteger messageCounter = new AtomicInteger();

private Map<Integer, String> responses = new ConcurrentHashMap<>();

private Map<Integer, CountDownLatch> latches = new ConcurrentHashMap<>();

public int getNextMessageCounter() {
int counter = messageCounter.incrementAndGet();
// MAX_VALUE used for error scenarios
if (counter == Integer.MAX_VALUE) {
counter = messageCounter.incrementAndGet();
}
latches.put(counter, new CountDownLatch(1));
return counter;
}

public void waitForMessage(int counter, long timeout, TimeUnit unit) {
CountDownLatch latch = latches.get(counter);
if (latch == null) {
return;
}
try {
if (!latch.await(timeout, unit)) {
Log.i(TAG, "Timed out while waiting for response for message " + counter);
}
} catch (InterruptedException e) {
Log.i(TAG, "Interrupted while waiting for response for message " + counter);
}
}

public void receivedMessageResponse(int counter, String response) {
CountDownLatch latch = latches.remove(counter);
if (latch == null) {
// no point adding response to memory if no one is going to read it.
return;
}
responses.put(counter, response);
latch.countDown();
}

public String readAndRemoveResponse(int counter) {
// caller should manage null values
return responses.remove(counter);
}
}
2 changes: 1 addition & 1 deletion examples/tv-app/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ shared_library("jni") {
"${chip_root}/examples/tv-app/tv-common",
"${chip_root}/src/app/server/java:jni",
"${chip_root}/src/lib",
"${chip_root}/src/lib/support/jsontlv",
"${chip_root}/third_party/inipp",
]

Expand Down Expand Up @@ -108,7 +109,6 @@ android_library("java") {
"java/src/com/tcl/chip/tvapp/ChannelManagerStub.java",
"java/src/com/tcl/chip/tvapp/Clusters.java",
"java/src/com/tcl/chip/tvapp/ContentAppEndpointManager.java",
"java/src/com/tcl/chip/tvapp/ContentAppEndpointManagerStub.java",
"java/src/com/tcl/chip/tvapp/ContentLaunchBrandingInformation.java",
"java/src/com/tcl/chip/tvapp/ContentLaunchManager.java",
"java/src/com/tcl/chip/tvapp/ContentLaunchManagerStub.java",
Expand Down
42 changes: 27 additions & 15 deletions examples/tv-app/android/java/AppImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@

#include "AppImpl.h"

#include "ContentAppCommandDelegate.h"
#include <app-common/zap-generated/attribute-id.h>
#include <app-common/zap-generated/cluster-id.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/CommandHandler.h>
#include <app/InteractionModelEngine.h>
#include <app/server/Dnssd.h>
#include <app/server/Server.h>
#include <app/util/af.h>
Expand Down Expand Up @@ -328,12 +330,12 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend
{
auto & app = mContentApps.at(i);

ChipLogProgress(DeviceLayer, " Looking next=%s ", app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId);
if (app.GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp))
ChipLogProgress(DeviceLayer, " Looking next=%s ", app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->applicationId);
if (app->GetApplicationBasicDelegate()->GetCatalogVendorApp()->Matches(vendorApp))
{
ContentAppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, Span<DataVersion>(gDataVersions[i]),
ContentAppPlatform::GetInstance().AddContentApp(app, &contentAppEndpoint, Span<DataVersion>(gDataVersions[i]),
Span<const EmberAfDeviceType>(gContentAppDeviceType));
return &app;
return app;
}
}
ChipLogProgress(DeviceLayer, "LoadContentAppByAppId NOT FOUND catalogVendorId=%d applicationId=%s ", vendorApp.catalogVendorId,
Expand All @@ -342,13 +344,13 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend
return nullptr;
}

EndpointId ContentAppFactoryImpl::AddContentApp(ContentAppImpl & app)
EndpointId ContentAppFactoryImpl::AddContentApp(ContentAppImpl * app)
{
DataVersion dataVersionBuf[ArraySize(contentAppClusters)];
EndpointId epId = ContentAppPlatform::GetInstance().AddContentApp(&app, &contentAppEndpoint, Span<DataVersion>(dataVersionBuf),
EndpointId epId = ContentAppPlatform::GetInstance().AddContentApp(app, &contentAppEndpoint, Span<DataVersion>(dataVersionBuf),
Span<const EmberAfDeviceType>(gContentAppDeviceType));
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl AddContentApp endpoint returned %d. Endpoint set %d", epId,
app.GetEndpointId());
app->GetEndpointId());
mContentApps.push_back(app);
return epId;
}
Expand Down Expand Up @@ -380,9 +382,9 @@ void ContentAppFactoryImpl::SendTestMessage(EndpointId epId, const char * messag
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl SendTestMessage called with message %s & endpointId %d", message, epId);
for (size_t i = 0; i < mContentApps.size(); ++i)
{
ContentAppImpl app = mContentApps.at(i);
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl checking app with endpointId %d", app.ContentApp::GetEndpointId());
if (app.GetEndpointId() == epId)
ContentAppImpl * app = mContentApps.at(i);
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl checking app with endpointId %d", app->GetEndpointId());
if (app->GetEndpointId() == epId)
{
ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl SendTestMessage endpoint found");
app::ConcreteCommandPath commandPath(epId, app::Clusters::ContentLauncher::Id,
Expand All @@ -391,8 +393,8 @@ void ContentAppFactoryImpl::SendTestMessage(EndpointId epId, const char * messag
app::CommandHandler commandHandler(&callback);
CommandResponseHelper<LaunchResponseType> helper(&commandHandler, commandPath);
chip::app::Clusters::ContentLauncher::Structs::BrandingInformation::Type branding;
app.GetContentLauncherDelegate()->HandleLaunchUrl(helper, CharSpan::fromCharString(message),
CharSpan::fromCharString("Temp Display"), branding);
app->GetContentLauncherDelegate()->HandleLaunchUrl(helper, CharSpan::fromCharString(message),
CharSpan::fromCharString("Temp Display"), branding);
}
}
}
Expand All @@ -402,11 +404,21 @@ void ContentAppFactoryImpl::SendTestMessage(EndpointId epId, const char * messag

#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED

CHIP_ERROR InitVideoPlayerPlatform(JNIMyUserPrompter * userPrompter)
CHIP_ERROR InitVideoPlayerPlatform(JNIMyUserPrompter * userPrompter, jobject contentAppEndpointManager)
{
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
ContentAppPlatform::GetInstance().SetupAppPlatform();
ContentAppPlatform::GetInstance().SetContentAppFactory(&gFactory);

ChipLogProgress(AppServer, "Starting registration of command handler delegates");
for (size_t i = 0; i < ArraySize(contentAppClusters); i++)
{
ContentAppCommandDelegate * delegate =
new ContentAppCommandDelegate(contentAppEndpointManager, contentAppClusters[i].clusterId);
chip::app::InteractionModelEngine::GetInstance()->RegisterCommandHandler(delegate);
ChipLogProgress(AppServer, "Registered command handler delegate for cluster %d", contentAppClusters[i].clusterId);
}

#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED

#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
Expand Down Expand Up @@ -443,8 +455,8 @@ EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const cha
const char * szApplicationVersion, jobject manager)
{
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
ContentAppImpl app =
ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "34567890", manager);
ContentAppImpl * app =
new ContentAppImpl(szVendorName, vendorId, szApplicationName, productId, szApplicationVersion, "20202021", manager);
ChipLogProgress(DeviceLayer, "AppImpl: AddContentApp vendorId=%d applicationName=%s ", vendorId, szApplicationName);
return gFactory.AddContentApp(app);
#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
Expand Down
Loading

0 comments on commit 7690b96

Please sign in to comment.