From 53d79a5b677a1736771f6f0d2f909950066409d6 Mon Sep 17 00:00:00 2001 From: Philip Gregor Date: Wed, 13 Dec 2023 16:54:25 -0800 Subject: [PATCH] tv-casting-app: simplified android discovery API --- .../app/CommissionerDiscoveryFragment.java | 56 ++++- .../com/chip/casting/app/MainActivity.java | 16 +- .../casting/util/GlobalCastingConstants.java | 3 +- .../util/PreferencesConfigurationManager.java | 72 +++++- .../com/matter/casting/DiscoveryExample.java | 174 ++++++++++---- .../matter/casting/InitializationExample.java | 11 +- .../com/chip/casting/DiscoveredNodeData.java | 10 + .../chip/casting/NsdDiscoveryListener.java | 19 +- .../com/chip/casting/NsdResolveListener.java | 14 +- .../jni/com/chip/casting/TvCastingApp.java | 31 ++- .../jni/com/chip/casting/VideoPlayer.java | 5 +- .../com/matter/casting/core/CastingApp.java | 39 +++ .../matter/casting/core/CastingPlayer.java | 48 ++-- .../casting/core/CastingPlayerDiscovery.java | 73 ++++-- .../casting/core/MatterCastingPlayer.java | 98 ++++++++ .../core/MatterCastingPlayerDiscovery.java | 50 ++++ .../src/main/jni/cpp/core/CastingApp-JNI.cpp | 6 +- .../cpp/core/CastingPlayerDiscovery-JNI.cpp | 226 ++++++++++++++++++ .../jni/cpp/core/CastingPlayerDiscovery-JNI.h | 41 ++++ .../support/CastingPlayerConverter-JNI.cpp | 94 ++++++++ .../cpp/support/CastingPlayerConverter-JNI.h | 41 ++++ examples/tv-casting-app/android/BUILD.gn | 8 + .../tv-casting-common/core/CastingApp.cpp | 4 + .../core/CastingPlayerDiscovery.cpp | 7 +- .../core/CastingPlayerDiscovery.h | 1 + 25 files changed, 1029 insertions(+), 118 deletions(-) create mode 100644 examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayer.java create mode 100644 examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayerDiscovery.java create mode 100644 examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.cpp create mode 100644 examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.h create mode 100644 examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.cpp create mode 100644 examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.h diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CommissionerDiscoveryFragment.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CommissionerDiscoveryFragment.java index dbb95e88806a92..1d04fd0d02c107 100644 --- a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CommissionerDiscoveryFragment.java +++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CommissionerDiscoveryFragment.java @@ -19,6 +19,8 @@ import com.chip.casting.MatterError; import com.chip.casting.SuccessCallback; import com.chip.casting.TvCastingApp; +import com.chip.casting.util.GlobalCastingConstants; +import com.matter.casting.DiscoveryExample; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -39,6 +41,9 @@ public class CommissionerDiscoveryFragment extends Fragment { private ScheduledExecutorService executor; public CommissionerDiscoveryFragment(TvCastingApp tvCastingApp) { + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment() called, constructor, tvCastingApp: " + + tvCastingApp); this.tvCastingApp = tvCastingApp; this.executor = Executors.newSingleThreadScheduledExecutor(); } @@ -50,6 +55,9 @@ public CommissionerDiscoveryFragment(TvCastingApp tvCastingApp) { * @return A new instance of fragment CommissionerDiscoveryFragment. */ public static CommissionerDiscoveryFragment newInstance(TvCastingApp tvCastingApp) { + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment newInstance() called with tvCastingApp: " + + tvCastingApp); return new CommissionerDiscoveryFragment(tvCastingApp); } @@ -67,6 +75,8 @@ public View onCreateView( @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onViewCreated() called"); super.onViewCreated(view, savedInstanceState); Button manualCommissioningButton = getView().findViewById(R.id.manualCommissioningButton); @@ -99,10 +109,15 @@ public void onClick(View v) { final ListView list = getActivity().findViewById(R.id.commissionerList); list.setAdapter(arrayAdapter); + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onViewCreated(), creating Callbacks"); this.successCallback = new SuccessCallback() { @Override public void handle(DiscoveredNodeData discoveredNodeData) { + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onViewCreated() successCallback called, discoveredNodeData: " + + discoveredNodeData); Log.d(TAG, "Discovered a Video Player Commissioner: " + discoveredNodeData); new Handler(Looper.getMainLooper()) .post( @@ -129,6 +144,8 @@ public void handle(DiscoveredNodeData discoveredNodeData) { new FailureCallback() { @Override public void handle(MatterError matterError) { + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onViewCreated() failureCallback called"); Log.e(TAG, "Error occurred during video player commissioner discovery: " + matterError); if (MatterError.DISCOVERY_SERVICE_LOST == matterError) { Log.d(TAG, "Attempting to restart service"); @@ -148,21 +165,44 @@ public void handle(MatterError matterError) { @Override public void onResume() { super.onResume(); + System.out.println("PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onResume() called"); Log.d(TAG, "Auto discovering"); - poller = - executor.scheduleAtFixedRate( - () -> { - tvCastingApp.discoverVideoPlayerCommissioners(successCallback, failureCallback); - }, - 0, - DISCOVERY_POLL_INTERVAL_MS, - TimeUnit.MILLISECONDS); + if (GlobalCastingConstants.ChipCastingSimplified) { + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onResume() Auto discovering... - SimplifiedAPI"); + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onResume() tvCastingApp: " + + tvCastingApp); + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onResume() calling DiscoveryExample.demoDiscovery()"); + DiscoveryExample.demoDiscovery(successCallback, failureCallback); + } else { + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onResume() Auto discovering... - Old API"); + poller = + executor.scheduleAtFixedRate( + () -> { + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onResume() calling TvCastingApp.discoverVideoPlayerCommissioners() with successCallback and failureCallback"); + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onResume() tvCastingApp: " + + tvCastingApp); + tvCastingApp.discoverVideoPlayerCommissioners(successCallback, failureCallback); + }, + 0, + DISCOVERY_POLL_INTERVAL_MS, // 15,000 ms + TimeUnit.MILLISECONDS); + + System.out.println( + "PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onResume() called, Auto discovering, pooling..."); + } } @Override public void onPause() { super.onPause(); + System.out.println("PHILIPGREGOR java-chip \tCommissionerDiscoveryFragment onPause() called"); tvCastingApp.stopVideoPlayerDiscovery(); poller.cancel(true); } diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/MainActivity.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/MainActivity.java index de1b547f929dd5..749c714ad3a86e 100644 --- a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/MainActivity.java +++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/MainActivity.java @@ -24,9 +24,11 @@ public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { + System.out.println("PHILIPGREGOR java-chip \tMainActivity onCreate() called"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - + System.out.println( + "PHILIPGREGOR java-chip \tMainActivity onCreate() calling InitializationExample.initAndStart() or MainActivity.initJni()"); boolean ret = GlobalCastingConstants.ChipCastingSimplified ? InitializationExample.initAndStart(this.getApplicationContext()).hasNoError() @@ -35,6 +37,7 @@ protected void onCreate(Bundle savedInstanceState) { Log.e(TAG, "Failed to initialize Matter TV casting library"); return; } + System.out.println("PHILIPGREGOR java-chip \tMainActivity onCreate() ret: " + ret); Fragment fragment = CommissionerDiscoveryFragment.newInstance(tvCastingApp); getSupportFragmentManager() @@ -45,31 +48,37 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void handleCommissioningButtonClicked(DiscoveredNodeData commissioner) { + System.out.println("PHILIPGREGOR java-chip \tMainActivity handleCommissioningButtonClicked()"); showFragment(ConnectionFragment.newInstance(tvCastingApp, commissioner)); } @Override public void handleCommissioningComplete() { + System.out.println("PHILIPGREGOR java-chip \tMainActivity handleCommissioningComplete()"); showFragment(SelectClusterFragment.newInstance(tvCastingApp)); } @Override public void handleContentLauncherSelected() { + System.out.println("PHILIPGREGOR java-chip \tMainActivity handleContentLauncherSelected()"); showFragment(ContentLauncherFragment.newInstance(tvCastingApp)); } @Override public void handleCertTestLauncherSelected() { + System.out.println("PHILIPGREGOR java-chip \tMainActivity handleCertTestLauncherSelected()"); showFragment(CertTestFragment.newInstance(tvCastingApp)); } @Override public void handleMediaPlaybackSelected() { + System.out.println("PHILIPGREGOR java-chip \tMainActivity handleMediaPlaybackSelected()"); showFragment(MediaPlaybackFragment.newInstance(tvCastingApp)); } @Override public void handleDisconnect() { + System.out.println("PHILIPGREGOR java-chip \tMainActivity handleDisconnect()"); showFragment(CommissionerDiscoveryFragment.newInstance(tvCastingApp)); } @@ -79,6 +88,10 @@ public void handleDisconnect() { * TvCastingApp */ private boolean initJni() { + System.out.println("PHILIPGREGOR java-chip \tMainActivity initJni() called - CHIP"); + + System.out.println( + "PHILIPGREGOR java-chip \tMainActivity initJni() calling TvCastingApp.getInstance()"); tvCastingApp = TvCastingApp.getInstance(); tvCastingApp.setDACProvider(new com.chip.casting.util.DACProviderStub()); @@ -97,6 +110,7 @@ private boolean initJni() { } private void showFragment(Fragment fragment, boolean showOnBack) { + System.out.println("PHILIPGREGOR java-chip \tMainActivity showFragment() called"); Log.d( TAG, "showFragment called with " + fragment.getClass().getSimpleName() + " and " + showOnBack); diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/GlobalCastingConstants.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/GlobalCastingConstants.java index d063cc7e6f78c5..926b520695b05b 100644 --- a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/GlobalCastingConstants.java +++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/GlobalCastingConstants.java @@ -6,5 +6,6 @@ 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 + // PHILIPGREGOR, change to false before commit } diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/PreferencesConfigurationManager.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/PreferencesConfigurationManager.java index 5f7cb1cb7a2a16..1d34e687f07b5c 100644 --- a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/PreferencesConfigurationManager.java +++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/util/PreferencesConfigurationManager.java @@ -34,8 +34,21 @@ public class PreferencesConfigurationManager implements ConfigurationManager { private SharedPreferences preferences; public PreferencesConfigurationManager(Context context, String preferenceFileKey) { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager() preferenceFileKey: " + + preferenceFileKey); preferences = context.getSharedPreferences(preferenceFileKey, Context.MODE_PRIVATE); + Map appMetadata = (Map) preferences.getAll(); + + for (Map.Entry entry : appMetadata.entrySet()) { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager() Key: " + + entry.getKey() + + " Value: " + + entry.getValue()); + } + try { String keyUniqueId = getKey(kConfigNamespace_ChipFactory, kConfigKey_UniqueId); if (!preferences.contains(keyUniqueId)) { @@ -52,32 +65,58 @@ public PreferencesConfigurationManager(Context context, String preferenceFileKey @Override public long readConfigValueLong(String namespace, String name) throws AndroidChipPlatformException { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager readConfigValueLong() namespace: " + + namespace + + " name: " + + name); String key = getKey(namespace, name); if (preferences.contains(key)) { long value = preferences.getLong(key, Long.MAX_VALUE); return value; } else { Log.d(TAG, "Key '" + key + "' not found in shared preferences"); - throw new AndroidChipPlatformException(); + // throw new AndroidChipPlatformException(); + return 0; } } @Override public String readConfigValueStr(String namespace, String name) throws AndroidChipPlatformException { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager readConfigValueStr() namespace: " + + namespace + + " name: " + + name); String key = getKey(namespace, name); - if (preferences.contains(key)) { - String value = preferences.getString(key, null); + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager readConfigValueStr() key: " + + key); + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager readConfigValueStr() preferences: " + + preferences.getAll().toString()); + if (preferences.contains(name)) { + String value = preferences.getString(name, null); + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager readConfigValueStr() value: " + + value); return value; } else { Log.d(TAG, "Key '" + key + "' not found in shared preferences"); - throw new AndroidChipPlatformException(); + // throw new AndroidChipPlatformException(); + return null; } } @Override public byte[] readConfigValueBin(String namespace, String name) throws AndroidChipPlatformException { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager readConfigValueBin() namespace: " + + namespace + + " name: " + + name); String key = getKey(namespace, name); if (preferences.contains(key)) { String value = preferences.getString(key, null); @@ -92,6 +131,11 @@ public byte[] readConfigValueBin(String namespace, String name) @Override public void writeConfigValueLong(String namespace, String name, long val) throws AndroidChipPlatformException { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager writeConfigValueLong() namespace: " + + namespace + + " name: " + + name); String key = getKey(namespace, name); preferences.edit().putLong(key, val).apply(); } @@ -99,6 +143,11 @@ public void writeConfigValueLong(String namespace, String name, long val) @Override public void writeConfigValueStr(String namespace, String name, String val) throws AndroidChipPlatformException { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager writeConfigValueStr() namespace: " + + namespace + + " name: " + + name); String key = getKey(namespace, name); preferences.edit().putString(key, val).apply(); } @@ -106,6 +155,11 @@ public void writeConfigValueStr(String namespace, String name, String val) @Override public void writeConfigValueBin(String namespace, String name, byte[] val) throws AndroidChipPlatformException { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager writeConfigValueBin() namespace: " + + namespace + + " name: " + + name); String key = getKey(namespace, name); if (val != null) { String valStr = Base64.getEncoder().encodeToString(val); @@ -117,6 +171,11 @@ public void writeConfigValueBin(String namespace, String name, byte[] val) @Override public void clearConfigValue(String namespace, String name) throws AndroidChipPlatformException { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager clearConfigValue() namespace: " + + namespace + + " name: " + + name); if (namespace != null && name != null) { preferences.edit().remove(getKey(namespace, name)).apply(); } else if (namespace != null && name == null) { @@ -138,6 +197,11 @@ public void clearConfigValue(String namespace, String name) throws AndroidChipPl @Override public boolean configValueExists(String namespace, String name) throws AndroidChipPlatformException { + System.out.println( + "PHILIPGREGOR java-chip \tPreferencesConfigurationManager configValueExists() namespace: " + + namespace + + " name: " + + name); return preferences.contains(getKey(namespace, name)); } diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/DiscoveryExample.java b/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/DiscoveryExample.java index f07ffea637418a..4de5009ae873a1 100644 --- a/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/DiscoveryExample.java +++ b/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/DiscoveryExample.java @@ -17,63 +17,153 @@ package com.matter.casting; import android.util.Log; +import com.chip.casting.DiscoveredNodeData; +import com.chip.casting.FailureCallback; +import com.chip.casting.SuccessCallback; import com.matter.casting.core.CastingPlayer; import com.matter.casting.core.CastingPlayerDiscovery; import com.matter.casting.core.CastingPlayerDiscovery.CastingPlayerChangeListener; +import com.matter.casting.core.MatterCastingPlayerDiscovery; +import com.matter.casting.support.MatterError; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class DiscoveryExample { - private static final ScheduledExecutorService executorService = - Executors.newSingleThreadScheduledExecutor(); + private static final String TAG = DiscoveryExample.class.getSimpleName(); + private static final ScheduledExecutorService executorService = + Executors.newSingleThreadScheduledExecutor(); + private static SuccessCallback successCallback = null; + private static FailureCallback failureCallback = null; - /** - * Implementation of a CastingPlayerChangeListener used to listen to changes in the discovered - * CastingPlayers - */ - private static final CastingPlayerChangeListener castingPlayerChangeListener = - new CastingPlayerChangeListener() { - private final String TAG = CastingPlayerChangeListener.class.getSimpleName(); + /** + * Implementation of a CastingPlayerChangeListener used to listen to changes in the discovered + * CastingPlayers + */ + // was private + public static final CastingPlayerChangeListener castingPlayerChangeListener = + new CastingPlayerChangeListener() { + private final String TAG = CastingPlayerChangeListener.class.getSimpleName(); - @Override - public void onChanged(CastingPlayer castingPlayer) { - Log.i(TAG, "Discovered changes to " + castingPlayer); - // consume changes to the provided castingPlayers - } + @Override + public void onAdded(CastingPlayer castingPlayer) { + if (castingPlayer != null) { + Log.i( + TAG, + "DiscoveryExample.CastingPlayerChangeListener.onAdded() Discovered CastingPlayer with ID: " + + castingPlayer.getId()); + } else { + Log.i(TAG, "DiscoveryExample.CastingPlayerChangeListener.onAdded()"); + // Attempt to invoke interface method on a null object reference will throw an error + Log.i( + TAG, + "DiscoveryExample.CastingPlayerChangeListener.onAdded() Discovered CastingPlayer with ID: " + + castingPlayer.getId()); + } + // TODO: In following PRs. Consume discovered CastingPlayer. + } - @Override - public void onAdded(CastingPlayer castingPlayer) { - Log.i(TAG, "Discovered " + castingPlayer); - // consume discovered castingPlayers - } + @Override + public void onChanged(CastingPlayer castingPlayer) { + Log.i( + TAG, + "DiscoveryExample.CastingPlayerChangeListener.onChanged() Discovered changes to CastingPlayer with ID: " + + castingPlayer.getId()); + // TODO: In following PRs. Consume changes to the provided CastingPlayer. + } - @Override - public void onRemoved(CastingPlayer castingPlayer) { - Log.i(TAG, "Removed " + castingPlayer); - // consume castingPlayers removed or lost from the network - } - }; + @Override + public void onRemoved(CastingPlayer castingPlayer) { + Log.i( + TAG, + "DiscoveryExample.CastingPlayerChangeListener.onRemoved() Removed CastingPlayer with ID: " + + castingPlayer.getId()); + // TODO: In following PRs. Consume CastingPlayer removed or lost from the network. + } + }; - public static void demoDiscovery() { - // Get the singleton instance of the PlayerDiscovery - CastingPlayerDiscovery discoverer = CastingPlayerDiscovery.getInstance(); + public static void demoDiscovery( + SuccessCallback discoverySuccessCallback, + FailureCallback discoveryFailureCallback) { + Log.i( + TAG, + "DiscoveryExample.demoDiscovery() called with discoverySuccessCallback and discoveryFailureCallback Callbacks"); + successCallback = discoverySuccessCallback; + failureCallback = discoveryFailureCallback; - // Add our castingPlayerChangeListener to listen to changes in the discovered CastingPlayers - discoverer.addCastingPlayerChangeListener(castingPlayerChangeListener); + // Get a singleton instance of the MatterCastingPlayerDiscovery + CastingPlayerDiscovery matterCastingPlayerDiscovery = + MatterCastingPlayerDiscovery.getInstance(); - // Start discovery - discoverer.startDiscovery(); + // Add the implemented CastingPlayerChangeListener to listen to changes in the discovered + // CastingPlayers + matterCastingPlayerDiscovery.addCastingPlayerChangeListener(castingPlayerChangeListener); - // After some time, stop discovering and remove our castingPlayerChangeListener from the set of - // listeners the CastingPlayerDiscoverer informs - executorService.schedule( - () -> { - discoverer.stopDiscovery(); - discoverer.removeCastingPlayerChangeListener(castingPlayerChangeListener); - }, - 30, - TimeUnit.SECONDS); + Log.i( + TAG, + "DiscoveryExample.demoDiscovery() calling MatterCastingPlayerDiscovery.startDiscovery()"); + // Start discovery + MatterError errStart = matterCastingPlayerDiscovery.startDiscovery(); + if (errStart.hasError()) { + Log.e( + TAG, + "DiscoveryExample.demoDiscovery() MatterCastingPlayerDiscovery.startDiscovery() called, errStart: " + + errStart); } -} \ No newline at end of file + // After some time, stop discovering and remove our CastingPlayerChangeListener from the set of + // listeners the CastingPlayerDiscoverer informs + executorService.schedule( + () -> { + Log.i( + TAG, + "DiscoveryExample.demoDiscovery() calling MatterCastingPlayerDiscovery.stopDiscovery()"); + // Stop discovery + MatterError errStop = matterCastingPlayerDiscovery.stopDiscovery(); + if (errStop.hasError()) { + Log.e( + TAG, + "DiscoveryExample.demoDiscovery() MatterCastingPlayerDiscovery.startDiscovery() called, errStop: " + + errStop); + } + matterCastingPlayerDiscovery.removeCastingPlayerChangeListener( + castingPlayerChangeListener); + Log.i(TAG, "DiscoveryExample.demoDiscovery() Complete after 30 seconds"); + }, + 30, + TimeUnit.SECONDS); + + // TODO + // After some time in discovery, call the getDiscoveredCastingPlayers function to get a list of + // discovered Casting Players + int delaySeconds = 15; + executorService.schedule( + () -> { + // List discoveredMatterCastingPlayers = new ArrayList(); + Log.i( + TAG, + "DiscoveryExample.demoDiscovery() calling MatterCastingPlayerDiscovery.getDiscoveredCastingPlayers() after " + + delaySeconds + + " seconds"); + List discoveredMatterCastingPlayers = + matterCastingPlayerDiscovery.getDiscoveredCastingPlayers(); + Log.i( + TAG, + "DiscoveryExample.demoDiscovery() Discovered " + + discoveredMatterCastingPlayers.size() + + " CastingPlayer(s): "); + int castingPlayerCount = 1; + for (CastingPlayer castingPlayer : discoveredMatterCastingPlayers) { + Log.i( + TAG, + "DiscoveryExample.demoDiscovery() Discovered CastingPlayer #" + + castingPlayerCount + + " ID: " + + castingPlayer.getId()); + castingPlayerCount++; + } + }, + delaySeconds, + TimeUnit.SECONDS); + } +} diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/InitializationExample.java b/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/InitializationExample.java index 0d2135e1a7bc66..211a48b70c3326 100644 --- a/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/InitializationExample.java +++ b/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/InitializationExample.java @@ -53,6 +53,8 @@ public byte[] get() { new DataProvider() { @Override public CommissionableData get() { + System.out.println( + "PHILIPGREGOR java-matter \tInitializationExample DataProvider commissionableDataProvider called"); // dummy values for demonstration only return new CommissionableData(20202021, 3874); } @@ -70,6 +72,8 @@ public CommissionableData get() { * @param applicationContext Given android.content.Context, initialize and start the CastingApp */ public static MatterError initAndStart(Context applicationContext) { + System.out.println( + "PHILIPGREGOR java-matter \tInitializationExample initAndStart() called - MATTER"); // Create an AppParameters object to pass in global casting parameters to the SDK final AppParameters appParameters = new AppParameters( @@ -86,15 +90,18 @@ public ConfigurationManager get() { dacProvider); // Initialize the SDK using the appParameters and check if it returns successfully + System.out.println( + "PHILIPGREGOR java-matter \tInitializationExample initAndStart() calling CastingApp.getInstance().initialize()"); MatterError err = CastingApp.getInstance().initialize(appParameters); if (err.hasError()) { Log.e(TAG, "Failed to initialize Matter CastingApp"); return err; } - + System.out.println( + "PHILIPGREGOR java-matter \tInitializationExample initAndStart() calling CastingApp.start()"); err = CastingApp.getInstance().start(); if (err.hasError()) { - Log.e(TAG, "Failed to start Matter CastingApp"); + Log.e(TAG, "PHILIPGREGOR Failed to start Matter CastingApp"); return err; } return err; diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/DiscoveredNodeData.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/DiscoveredNodeData.java index a66d48a57c0e2a..9246bff0dd1993 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/DiscoveredNodeData.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/DiscoveredNodeData.java @@ -54,6 +54,9 @@ public class DiscoveredNodeData { private VideoPlayer connectableVideoPlayer; public DiscoveredNodeData(NsdServiceInfo serviceInfo) { + System.out.println( + "PHILIPGREGOR jni-chip \tDiscoveredNodeData() constructor called, NsdServiceInfo: " + + serviceInfo); Map attributes = serviceInfo.getAttributes(); if (attributes != null) { @@ -104,9 +107,16 @@ public DiscoveredNodeData(NsdServiceInfo serviceInfo) { } this.port = serviceInfo.getPort(); this.numIPs = 1; + if (serviceInfo.getServiceName() != null) { + this.instanceName = serviceInfo.getServiceName(); + } else { + Log.e(TAG, "Service name was null"); + } } public DiscoveredNodeData(VideoPlayer player) { + System.out.println( + "PHILIPGREGOR jni-chip \tDiscoveredNodeData() constructor called, VideoPlayer: " + player); this.connectableVideoPlayer = player; this.instanceName = player.getInstanceName(); this.hostName = player.getHostName(); diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/NsdDiscoveryListener.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/NsdDiscoveryListener.java index 1a9556e5fdb6d2..f7b6659664d9fd 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/NsdDiscoveryListener.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/NsdDiscoveryListener.java @@ -41,10 +41,11 @@ public NsdDiscoveryListener( NsdManager nsdManager, String targetServiceType, List deviceTypeFilter, - List preCommissionedVideoPlayers, + List preCommissionedVideoPlayers, // what is this? SuccessCallback successCallback, FailureCallback failureCallback, NsdManagerServiceResolver.NsdManagerResolverAvailState nsdManagerResolverAvailState) { + System.out.println("PHILIPGREGOR jni-chip \tNsdDiscoveryListener() constructor called"); this.nsdManager = nsdManager; this.targetServiceType = targetServiceType; this.deviceTypeFilter = deviceTypeFilter; @@ -52,26 +53,38 @@ public NsdDiscoveryListener( this.successCallback = successCallback; this.failureCallback = failureCallback; this.nsdManagerResolverAvailState = nsdManagerResolverAvailState; + // this.resolutionExecutor = Executors.newSingleThreadExecutor(); } @Override public void onDiscoveryStarted(String regType) { + System.out.println("PHILIPGREGOR jni-chip \tNsdDiscoveryListener onDiscoveryStarted() called"); Log.d(TAG, "Service discovery started. regType: " + regType); } @Override public void onServiceFound(NsdServiceInfo service) { + System.out.println( + "PHILIPGREGOR jni-chip \tNsdDiscoveryListener onServiceFound() called, service: " + + service); + // this.successCallback.handle(service); + this.resolutionExecutor.execute( new Runnable() { @Override public void run() { + System.out.println( + "PHILIPGREGOR jni-chip \tNsdDiscoveryListener onServiceFound() discovered service: " + + service); Log.d(TAG, "Service discovery success. " + service); if (service.getServiceType().equals(targetServiceType)) { if (nsdManagerResolverAvailState != null) { nsdManagerResolverAvailState.acquireResolver(); } + System.out.println( + "PHILIPGREGOR jni-chip \tNsdDiscoveryListener onServiceFound() calling NsdManager.resolveService()"); Log.d(TAG, "Calling NsdManager.resolveService for " + service); nsdManager.resolveService( service, @@ -79,7 +92,7 @@ public void run() { nsdManager, deviceTypeFilter, preCommissionedVideoPlayers, - successCallback, + successCallback, // this calls handle() failureCallback, nsdManagerResolverAvailState, 1)); @@ -94,6 +107,8 @@ public void run() { public void onServiceLost(NsdServiceInfo service) { // When the network service is no longer available. // Internal bookkeeping code goes here. + System.out.println( + "PHILIPGREGOR jni-chip \tNsdDiscoveryListener onServiceLost() called, service: " + service); Log.e(TAG, "Service lost: " + service); failureCallback.handle(MatterError.DISCOVERY_SERVICE_LOST); } diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/NsdResolveListener.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/NsdResolveListener.java index 253dbee0968874..1e30552a311bbe 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/NsdResolveListener.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/NsdResolveListener.java @@ -48,6 +48,7 @@ public NsdResolveListener( FailureCallback failureCallback, NsdManagerServiceResolver.NsdManagerResolverAvailState nsdManagerResolverAvailState, int resolutionAttemptNumber) { + System.out.println("PHILIPGREGOR jni-chip \tNsdResolveListener() constructor called"); this.nsdManager = nsdManager; this.deviceTypeFilter = deviceTypeFilter; this.preCommissionedVideoPlayers = preCommissionedVideoPlayers; @@ -64,14 +65,19 @@ public NsdResolveListener( @Override public void onServiceResolved(NsdServiceInfo serviceInfo) { + System.out.println( + "PHILIPGREGOR jni-chip \tNsdResolveListener onServiceResolved() called, serviceInfo: " + + serviceInfo); DiscoveredNodeData discoveredNodeData = new DiscoveredNodeData(serviceInfo); Log.d(TAG, "DiscoveredNodeData resolved: " + discoveredNodeData); - if (nsdManagerResolverAvailState != null) { nsdManagerResolverAvailState.signalFree(); } if (isPassingDeviceTypeFilter(discoveredNodeData)) { + System.out.println( + "PHILIPGREGOR jni-chip \tNsdResolveListener onServiceResolved() calling addCommissioningInfo(), DiscoveredNodeData: " + + discoveredNodeData); addCommissioningInfo(discoveredNodeData); successCallback.handle(discoveredNodeData); } else { @@ -84,6 +90,9 @@ public void onServiceResolved(NsdServiceInfo serviceInfo) { @Override public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { + System.out.println( + "PHILIPGREGOR jni-chip \tNsdResolveListener onResolveFailed() called, serviceInfo " + + serviceInfo); if (nsdManagerResolverAvailState != null) { if (errorCode != NsdManager.FAILURE_ALREADY_ACTIVE || resolutionAttemptNumber >= MAX_RESOLUTION_ATTEMPTS) { @@ -142,6 +151,9 @@ private boolean isPassingDeviceTypeFilter(DiscoveredNodeData discoveredNodeData) } private void addCommissioningInfo(DiscoveredNodeData discoveredNodeData) { + System.out.println( + "PHILIPGREGOR jni-chip \tNsdResolveListener addCommissioningInfo() called, discoveredNodeData: " + + discoveredNodeData); if (preCommissionedVideoPlayers != null) { for (VideoPlayer videoPlayer : preCommissionedVideoPlayers) { if (videoPlayer.isSameAs(discoveredNodeData)) { diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/TvCastingApp.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/TvCastingApp.java index 24852986768e7c..47312facd03590 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/TvCastingApp.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/TvCastingApp.java @@ -63,6 +63,7 @@ public class TvCastingApp { private TvCastingApp() {} public static TvCastingApp getInstance() { + System.out.println("PHILIPGREGOR jni-chip \tTvCastingApp getInstance() called"); if (sInstance == null) { sInstance = new TvCastingApp(); } @@ -70,6 +71,7 @@ public static TvCastingApp getInstance() { } public boolean initApp(Context applicationContext, AppParameters appParameters) { + System.out.println("PHILIPGREGOR jni-chip \tTvCastingApp initApp() called"); if (applicationContext == null || appParameters == null || appParameters.getConfigurationManager() == null) { @@ -130,7 +132,11 @@ public boolean initApp(Context applicationContext, AppParameters appParameters) public void discoverVideoPlayerCommissioners( SuccessCallback discoverySuccessCallback, FailureCallback discoveryFailureCallback) { + System.out.println( + "PHILIPGREGOR jni-chip \tTvCastingApp discoverVideoPlayerCommissioners() called - before lock"); synchronized (discoveryLock) { + System.out.println( + "PHILIPGREGOR jni-chip \tTvCastingApp discoverVideoPlayerCommissioners() called - after lock"); Log.d(TAG, "TvCastingApp.discoverVideoPlayerCommissioners called"); if (this.discoveryStarted) { @@ -138,8 +144,10 @@ public void discoverVideoPlayerCommissioners( stopVideoPlayerDiscovery(); } + // Why is this needed? List preCommissionedVideoPlayers = readCachedVideoPlayers(); + // Is this used? WifiManager wifiManager = (WifiManager) applicationContext.getSystemService(Context.WIFI_SERVICE); multicastLock = wifiManager.createMulticastLock("multicastLock"); @@ -148,26 +156,36 @@ public void discoverVideoPlayerCommissioners( nsdManager = (NsdManager) applicationContext.getSystemService(Context.NSD_SERVICE); discoveredPlayers = new ArrayList<>(); + System.out.println( + "PHILIPGREGOR jni-chip \tTvCastingApp discoverVideoPlayerCommissioners() calling NsdDiscoveryListener()"); nsdDiscoveryListener = new NsdDiscoveryListener( nsdManager, DISCOVERY_TARGET_SERVICE_TYPE, DISCOVERY_TARGET_DEVICE_TYPE_FILTER, - preCommissionedVideoPlayers, + preCommissionedVideoPlayers, // why is this needed? new SuccessCallback() { @Override - public void handle(DiscoveredNodeData commissioner) { - Log.d(TAG, "Discovered commissioner added " + commissioner); - discoveredPlayers.add(commissioner); - discoverySuccessCallback.handle(commissioner); + public void handle(DiscoveredNodeData commissioner) { // the Casting Player + System.out.println( + "PHILIPGREGOR jni-chip \tTvCastingApp discoverVideoPlayerCommissioners() nsdDiscoveryListener() Discovered commissioner added: " + + commissioner); + Log.d(TAG, "Discovered commissioner added: " + commissioner); + discoveredPlayers.add( + commissioner); // Add the commissioner/CastingPlayer to our list + discoverySuccessCallback.handle(commissioner); // callback to display on UI } }, discoveryFailureCallback, nsdManagerResolverAvailState); + // Start discovery nsdManager.discoverServices( DISCOVERY_TARGET_SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, nsdDiscoveryListener); + Log.d(TAG, "TvCastingApp.discoverVideoPlayerCommissioners started"); + System.out.println( + "PHILIPGREGOR jni-chip \tTvCastingApp discoverVideoPlayerCommissioners() started"); /** * Surface players (as DiscoveredNodeData objects on discoverySuccessCallback) that we @@ -206,6 +224,8 @@ public void handle(DiscoveredNodeData commissioner) { private void reportSleepingVideoPlayerCommissioners( List cachedVideoPlayers, SuccessCallback discoverySuccessCallback) { + System.out.println( + "PHILIPGREGOR jni-chip \tTvCastingApp reportSleepingVideoPlayerCommissioners() called"); Log.d( TAG, "TvCastingApp.reportSleepingVideoPlayerCommissioners called with commissioner count " @@ -269,6 +289,7 @@ public boolean test(DiscoveredNodeData discoveredNodeData) { private native boolean WasRecentlyDiscoverable(VideoPlayer player); public void stopVideoPlayerDiscovery() { + System.out.println("PHILIPGREGOR jni-chip \tTvCastingApp stopVideoPlayerDiscovery() called"); synchronized (discoveryLock) { Log.d(TAG, "TvCastingApp trying to stop video player discovery"); if (this.discoveryStarted diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/VideoPlayer.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/VideoPlayer.java index fab9b027a78cc4..443a283ca885c2 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/VideoPlayer.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/VideoPlayer.java @@ -57,12 +57,13 @@ public VideoPlayer( int numIPs, List ipAddresses, String hostName, - String instanceName, + String name, // was instanceName int port, long lastDiscoveredMs, String MACAddress, boolean isAsleep, boolean isConnected) { + this.nodeId = nodeId; this.fabricIndex = fabricIndex; this.deviceName = deviceName; @@ -76,7 +77,7 @@ public VideoPlayer( this.hostName = hostName; this.MACAddress = MACAddress; this.lastDiscoveredMs = lastDiscoveredMs; - this.instanceName = instanceName; + this.instanceName = name; // was instanceName this.port = port; this.isAsleep = isAsleep; this.isInitialized = true; diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingApp.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingApp.java index 0823920503d9db..118d68a932aa93 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingApp.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingApp.java @@ -48,18 +48,28 @@ public final class CastingApp { private CastingApp() {} public static CastingApp getInstance() { + System.out.println("PHILIPGREGOR jni-matter \tCastingApp getInstance() called"); if (sInstance == null) { sInstance = new CastingApp(); } return sInstance; } + public Context getApplicationContext() { + System.out.println("PHILIPGREGOR jni-matter \tCastingApp getApplicationContext() called"); + return appParameters.getApplicationContext(); + }; + /** * Initializes the CastingApp with appParameters * * @param appParameters */ public MatterError initialize(AppParameters appParameters) { + System.out.println("PHILIPGREGOR jni-matter \tCastingApp initialize() called"); + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp initialize() appParameters: " + + appParameters.toString()); Log.i(TAG, "CastingApp.initialize called"); if (mState != CastingAppState.UNINITIALIZED) { return MatterError.CHIP_ERROR_INCORRECT_STATE; @@ -80,7 +90,27 @@ public MatterError initialize(AppParameters appParameters) { new ChipMdnsCallbackImpl(), new DiagnosticDataProviderImpl(applicationContext)); + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp initialize() calling getCommissionableDataProvider()"); CommissionableData commissionableData = appParameters.getCommissionableDataProvider().get(); + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp initialize() commissionableData: " + + commissionableData.toString()); + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp initialize() commissionableDataget.Spake2pVerifierBase64(): " + + commissionableData.getSpake2pVerifierBase64()); + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp initialize() commissionableDataget.Spake2pSaltBase64(): " + + commissionableData.getSpake2pSaltBase64()); + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp initialize() commissionableDataget.Spake2pIterationCount(): " + + commissionableData.getSpake2pIterationCount()); + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp initialize() commissionableDataget.SetupPasscode(): " + + commissionableData.getSetupPasscode()); + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp initialize() commissionableDataget.Discriminator(): " + + commissionableData.getDiscriminator()); boolean updated = chipPlatform.updateCommissionableDataProviderData( commissionableData.getSpake2pVerifierBase64(), @@ -94,6 +124,8 @@ public MatterError initialize(AppParameters appParameters) { return MatterError.CHIP_ERROR_INVALID_ARGUMENT; } + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp initialize() calling finishInitialization() JNI_METHOD"); MatterError err = finishInitialization(appParameters); if (err.hasNoError()) { @@ -107,6 +139,8 @@ public MatterError initialize(AppParameters appParameters) { * Starts the Matter server that the CastingApp runs on and registers all the necessary delegates */ public MatterError start() { + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp start() called, starting Matter server"); Log.i(TAG, "CastingApp.start called"); if (mState != CastingAppState.NOT_RUNNING) { return MatterError.CHIP_ERROR_INCORRECT_STATE; @@ -118,10 +152,14 @@ public MatterError start() { return MatterError.CHIP_ERROR_INCORRECT_STATE; } + System.out.println( + "PHILIPGREGOR jni-matter \tCastingApp start(), calling finishStartup() JNI_METHOD"); MatterError err = finishStartup(); if (err.hasNoError()) { mState = CastingAppState.RUNNING; // CastingApp started successfully, set state to RUNNING + System.out.println("PHILIPGREGOR jni-matter \tCastingApp start(), mState: " + mState); } + System.out.println("PHILIPGREGOR jni-matter \tCastingApp start(), err: " + err); return err; } @@ -131,6 +169,7 @@ public MatterError start() { * @return */ public MatterError stop() { + System.out.println("PHILIPGREGOR jni-matter \tCastingApp stop() called"); Log.i(TAG, "CastingApp.stop called"); if (mState != CastingAppState.RUNNING) { return MatterError.CHIP_ERROR_INCORRECT_STATE; diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayer.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayer.java index 7a49bf3482c49a..23eecbc80b34e5 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayer.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayer.java @@ -17,34 +17,34 @@ package com.matter.casting.core; public interface CastingPlayer { - String getId(); + String getId(); - String getName(); + String getName(); - String getVendorId(); + long getVendorId(); - String getProductId(); + long getProductId(); - String getType(); + long getType(); - // TODO -// List getEndpoints(); -// -// ConnectionState getConnectionState(); -// -// CompletableFuture connect(long timeout); + // TODO: To be implemented in a following PRs related to player connection implementation. + // List getEndpoints(); + // + // ConnectionState getConnectionState(); + // + // CompletableFuture connect(long timeout); -// static class ConnectionState extends Observable { -// private boolean connected; -// -// void setConnected(boolean connected) { -// this.connected = connected; -// setChanged(); -// notifyObservers(this.connected); -// } -// -// boolean isConnected() { -// return connected; -// } -// } + // static class ConnectionState extends Observable { + // private boolean connected; + // + // void setConnected(boolean connected) { + // this.connected = connected; + // setChanged(); + // notifyObservers(this.connected); + // } + // + // boolean isConnected() { + // return connected; + // } + // } } \ No newline at end of file diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayerDiscovery.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayerDiscovery.java index 0684bc37199c93..19b09a1de2cf68 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayerDiscovery.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayerDiscovery.java @@ -16,35 +16,66 @@ */ package com.matter.casting.core; +import android.util.Log; import java.util.List; public interface CastingPlayerDiscovery { - static CastingPlayerDiscovery getInstance() { - return null; - } - List getDiscoveredCastingPlayers(); + // TODO should be getCastingPlayers + List getDiscoveredCastingPlayers(); - /** - * - */ - void startDiscovery(); + com.matter.casting.support.MatterError startDiscovery(); - void stopDiscovery(); + com.matter.casting.support.MatterError stopDiscovery(); - /** - * - * @param listener - */ - void addCastingPlayerChangeListener(CastingPlayerChangeListener listener); + void addCastingPlayerChangeListener(CastingPlayerChangeListener listener); - void removeCastingPlayerChangeListener(CastingPlayerChangeListener listener); + void removeCastingPlayerChangeListener(CastingPlayerChangeListener listener); - interface CastingPlayerChangeListener { - void onChanged(CastingPlayer castingPlayer); + // The Casting Client discovers CastingPlayers using Matter Commissioner discovery over DNS-SD by + // listening for + // CastingPlayer events as they are discovered, updated, or lost from the network. + abstract class CastingPlayerChangeListener { + static final String TAG = CastingPlayerChangeListener.class.getSimpleName(); - void onAdded(CastingPlayer castingPlayer); + public abstract void onAdded(CastingPlayer castingPlayer); - void onRemoved(CastingPlayer castingPlayer); - } -} \ No newline at end of file + public abstract void onChanged(CastingPlayer castingPlayer); + + public abstract void onRemoved(CastingPlayer castingPlayer); + + protected final void _onAdded(CastingPlayer castingPlayer) { + try { + onAdded(castingPlayer); + } catch (Throwable t) { + Log.e( + TAG, + // TODO: REMOVE CLASS NAME LOGGED + "CastingPlayerChangeListener::onAdded() Caught an unhandled Throwable from the client: " + + t); + } + }; + + protected final void _onChanged(CastingPlayer castingPlayer) { + try { + onChanged(castingPlayer); + } catch (Throwable t) { + Log.e( + TAG, + "CastingPlayerChangeListener::onChanged() Caught an unhandled Throwable from the client: " + + t); + } + }; + + protected final void _onRemoved(CastingPlayer castingPlayer) { + try { + onRemoved(castingPlayer); + } catch (Throwable t) { + Log.e( + TAG, + "CastingPlayerChangeListener::onRemoved() Caught an unhandled Throwable from the client: " + + t); + } + }; + } +} diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayer.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayer.java new file mode 100644 index 00000000000000..53f3229a8b61d5 --- /dev/null +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayer.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 com.matter.casting.core; + +import java.net.InetAddress; +import java.util.List; + +public class MatterCastingPlayer implements CastingPlayer { + private boolean connected; + private String id; + private String deviceName; + private String hostName; + private String instanceName; + private int numIPs; + private List ipAddresses; + private int port; + private long productId; + private long vendorId; + private long deviceType; + + public MatterCastingPlayer( + boolean connected, + String id, + String hostName, + String deviceName, + String instanceName, + int numIPs, + List ipAddresses, + int port, + long productId, + long vendorId, + long deviceType + ) { + System.out.println("MatterCastingPlayer() constructor, connected: " + connected); + System.out.println("MatterCastingPlayer() constructor, id: " + id); + System.out.println("MatterCastingPlayer() constructor, hostName: " + hostName); + System.out.println("MatterCastingPlayer() constructor, deviceName: " + deviceName); + System.out.println("MatterCastingPlayer() constructor, instanceName: " + instanceName); + System.out.println("MatterCastingPlayer() constructor, numIPs: " + numIPs); + System.out.println("MatterCastingPlayer() constructor, ipAddresses: " + ipAddresses); + System.out.println("MatterCastingPlayer() constructor, port: " + port); + System.out.println("MatterCastingPlayer() constructor, productId: " + productId); + System.out.println("MatterCastingPlayer() constructor, vendorId: " + vendorId); + System.out.println("MatterCastingPlayer() constructor, deviceType: " + deviceType); + this.connected = connected; + this.id = id; + this.hostName = hostName; + this.deviceName = deviceName; + this.instanceName = instanceName; + this.numIPs = numIPs; + this.ipAddresses = ipAddresses; + this.port = port; + this.productId = productId; + this.vendorId = vendorId; + this.deviceType = deviceType; + } + + @Override + public String getName() { + return this.instanceName; + } + + @Override + public String getId() { + return this.id; + } + + @Override + public long getVendorId() { + return this.vendorId; + } + + @Override + public long getProductId() { + return this.productId; + } + + @Override + public long getType() { + return this.deviceType; + } + + // TODO: Add other getters. +} diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayerDiscovery.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayerDiscovery.java new file mode 100644 index 00000000000000..bd8313052b4656 --- /dev/null +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayerDiscovery.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 com.matter.casting.core; + +import android.util.Log; + +import java.util.List; + +public final class MatterCastingPlayerDiscovery implements CastingPlayerDiscovery { + private static final String TAG = MatterCastingPlayerDiscovery.class.getSimpleName(); + private static MatterCastingPlayerDiscovery matterCastingPlayerDiscoveryInstance; + + // Methods: + public static MatterCastingPlayerDiscovery getInstance() { + Log.i(TAG, "MatterCastingPlayerDiscovery.getInstance() called"); + if (matterCastingPlayerDiscoveryInstance == null) { + matterCastingPlayerDiscoveryInstance = new MatterCastingPlayerDiscovery(); + } + return matterCastingPlayerDiscoveryInstance; + }; + + @Override + public native List getDiscoveredCastingPlayers(); + + @Override + public native com.matter.casting.support.MatterError startDiscovery(); + + @Override + public native com.matter.casting.support.MatterError stopDiscovery(); + + @Override + public native void addCastingPlayerChangeListener(CastingPlayerChangeListener listener); + + @Override + public native void removeCastingPlayerChangeListener(CastingPlayerChangeListener listener); +} diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingApp-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingApp-JNI.cpp index 984287af8c30fd..f3ff70d51f9434 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingApp-JNI.cpp +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingApp-JNI.cpp @@ -44,7 +44,7 @@ jobject extractJAppParameter(jobject jAppParameters, const char * methodName, co JNI_METHOD(jobject, finishInitialization)(JNIEnv *, jobject, jobject jAppParameters) { chip::DeviceLayer::StackLock lock; - ChipLogProgress(AppServer, "JNI_METHOD CastingAppJNI.finishInitialization called"); + ChipLogProgress(AppServer, "JNI_METHOD CastingApp-JNI::finishInitialization() called"); VerifyOrReturnValue(jAppParameters != nullptr, support::createJMatterError(CHIP_ERROR_INVALID_ARGUMENT)); CHIP_ERROR err = CHIP_NO_ERROR; @@ -78,7 +78,7 @@ JNI_METHOD(jobject, finishInitialization)(JNIEnv *, jobject, jobject jAppParamet JNI_METHOD(jobject, finishStartup)(JNIEnv *, jobject) { chip::DeviceLayer::StackLock lock; - ChipLogProgress(AppServer, "JNI_METHOD CastingAppJNI.finishStartup called"); + ChipLogProgress(AppServer, "JNI_METHOD CastingApp-JNI::finishStartup() called"); auto & server = chip::Server::GetInstance(); // TODO: Set AppDelegate @@ -99,7 +99,7 @@ JNI_METHOD(jobject, finishStartup)(JNIEnv *, jobject) jobject extractJAppParameter(jobject jAppParameters, const char * methodName, const char * methodSig) { - ChipLogProgress(AppServer, "JNI_METHOD extractJAppParameter called"); + ChipLogProgress(AppServer, "JNI_METHOD CastingApp-JNI::extractJAppParameter() called"); JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread(); jclass jAppParametersClass; diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.cpp new file mode 100644 index 00000000000000..3fa41d7d5a91ca --- /dev/null +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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. + * + */ + +#include "CastingPlayerDiscovery-JNI.h" + +#include "../JNIDACProvider.h" +#include "../support/ErrorConverter-JNI.h" +#include "../support/CastingPlayerConverter-JNI.h" +#include "../support/RotatingDeviceIdUniqueIdProvider-JNI.h" +#include "core/CastingApp.h" // from tv-casting-common +#include "core/CastingPlayerDiscovery.h" // from tv-casting-common + +#include +#include +#include +#include +#include + +using namespace chip; + +#define JNI_METHOD(RETURN, METHOD_NAME) extern "C" JNIEXPORT RETURN JNICALL Java_com_matter_casting_core_MatterCastingPlayerDiscovery_##METHOD_NAME + +namespace matter { +namespace casting { +namespace core { + +/** +* @brief React to CastingPlayer discovery results with this sigleton +*/ +class DiscoveryDelegateImpl : public DiscoveryDelegate +{ +private: + DiscoveryDelegateImpl(){} + static DiscoveryDelegateImpl * discoveryDelegateImplSingletonInstance; + DiscoveryDelegateImpl(DiscoveryDelegateImpl & other)= delete; + void operator=(const DiscoveryDelegateImpl &) = delete; + +public: + jobject castingPlayerChangeListenerJavaObject = nullptr; + jmethodID onAddedCallbackJavaMethodID = nullptr; + jmethodID onChangedCallbackJavaMethodID = nullptr; + // jmethodID onRemovedCallbackJavaMethodID = nullptr; + + static DiscoveryDelegateImpl * GetInstance() + { + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::GetInstance() called"); + if (DiscoveryDelegateImpl::discoveryDelegateImplSingletonInstance == nullptr) + { + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::GetInstance() creating new singleton instance"); + DiscoveryDelegateImpl::discoveryDelegateImplSingletonInstance = new DiscoveryDelegateImpl(); + } + return DiscoveryDelegateImpl::discoveryDelegateImplSingletonInstance; + } + + void HandleOnAdded(matter::casting::memory::Strong player) override + { + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() called with CastingPlayer:"); + player->LogDetail(); + + if (castingPlayerChangeListenerJavaObject == nullptr || onAddedCallbackJavaMethodID == nullptr) { + ChipLogError(AppServer, "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() Warning: Not specified, CastingPlayerChangeListener == nullptr"); + } else { + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + jobject matterCastingPlayerJavaObject = support::createJCastingPlayer(player); + if (matterCastingPlayerJavaObject == nullptr){ + ChipLogError(AppServer, "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() Warning: Could not create CastingPlayer jobject"); + } else { + // Call the Java callback + env->CallVoidMethod(castingPlayerChangeListenerJavaObject, onAddedCallbackJavaMethodID, matterCastingPlayerJavaObject); + } + } + } + + void HandleOnUpdated(matter::casting::memory::Strong player) override + { + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI DiscoveryDelegateImpl::HandleOnUpdated() called with CastingPlayer:"); + player->LogDetail(); + + // Add implementaion + + } + + // TODO: In following PRs. Implement HandleOnRemoved after implemented in tv-casting-commom CastingPlayerDiscovery.h/cpp + // void HandleOnRemoved(matter::casting::memory::Strong player) override + // { + // ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI DiscoveryDelegateImpl::HandleOnRemoved() called with CastingPlayer:"); + // player->LogDetail(); + // } +}; + +// Initialize the static instance to nullptr +DiscoveryDelegateImpl * DiscoveryDelegateImpl::discoveryDelegateImplSingletonInstance = nullptr; + +JNI_METHOD(jobject, startDiscovery)(JNIEnv * env, jobject) +{ + chip::DeviceLayer::StackLock lock; + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::startDiscovery() called"); + DiscoveryDelegateImpl * delegate = DiscoveryDelegateImpl::GetInstance(); + CastingPlayerDiscovery::GetInstance()->SetDelegate(delegate); + + // Start CastingPlayer discovery + const uint64_t kTargetPlayerDeviceType = 35; // 35 represents device type of Matter Video Player + CHIP_ERROR err = CastingPlayerDiscovery::GetInstance()->StartDiscovery(kTargetPlayerDeviceType); + ChipLogError(AppServer, "CastingPlayerDiscovery-JNI startDiscovery() err: %" CHIP_ERROR_FORMAT, err.Format()); + + // TODO: Verify errorr returned? + VerifyOrReturnValue(err == CHIP_NO_ERROR, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); + return support::createJMatterError(CHIP_NO_ERROR); +} + +JNI_METHOD(jobject, stopDiscovery)(JNIEnv * env, jobject) +{ + chip::DeviceLayer::StackLock lock; + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::stopDiscovery() called"); + + // Stop CastingPlayer discovery + CHIP_ERROR err = CastingPlayerDiscovery::GetInstance()->StopDiscovery(); + + // TODO: Verify errorr returned? + VerifyOrReturnValue(err == CHIP_NO_ERROR, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); + return support::createJMatterError(CHIP_NO_ERROR); +} + +// TODO: RETURN A MATTER ERROR +JNI_METHOD(jobject, addCastingPlayerChangeListener)(JNIEnv * env, jobject, jobject castingPlayerChangeListenerJavaObject) +{ + chip::DeviceLayer::StackLock lock; + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::addCastingPlayerChangeListener() called"); + VerifyOrReturnValue(castingPlayerChangeListenerJavaObject != nullptr, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); + + // Get the class and method IDs for the CastingPlayerChangeListener Java class + jclass castingPlayerChangeListenerJavaClass = env->GetObjectClass(castingPlayerChangeListenerJavaObject); + VerifyOrReturnValue(castingPlayerChangeListenerJavaClass != nullptr, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); + jmethodID onAddedJavaMethodID = env->GetMethodID(castingPlayerChangeListenerJavaClass, "_onAdded", "(Lcom/matter/casting/core/CastingPlayer;)V"); + VerifyOrReturnValue(onAddedJavaMethodID != nullptr, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); + jmethodID onChangedJavaMethodID = env->GetMethodID(castingPlayerChangeListenerJavaClass, "_onChanged", "(Lcom/matter/casting/core/CastingPlayer;)V"); + VerifyOrReturnValue(onChangedJavaMethodID != nullptr, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); + //jmethodID onRemovedJavaMethodID = env->GetMethodID(castingPlayerChangeListenerJavaClass, "_onRemoved", "(Lcom/matter/casting/core/CastingPlayer;)V"); + //VerifyOrReturnValue(onRemovedJavaMethodID != nullptr, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); + + // Set Java callbacks in the DiscoveryDelegateImpl Singleton + DiscoveryDelegateImpl * delegate = DiscoveryDelegateImpl::GetInstance(); + delegate->castingPlayerChangeListenerJavaObject = env->NewGlobalRef(castingPlayerChangeListenerJavaObject); + delegate->onAddedCallbackJavaMethodID = onAddedJavaMethodID; + delegate->onChangedCallbackJavaMethodID = onChangedJavaMethodID; + //delegate->onRemovedCallbackJavaMethodID = onRemovedJavaMethodID; + + return support::createJMatterError(CHIP_NO_ERROR); +} + +JNI_METHOD(jobject, removeCastingPlayerChangeListener)(JNIEnv * env, jobject, jobject castingPlayerChangeListenerJavaObject) +{ + chip::DeviceLayer::StackLock lock; + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::removeCastingPlayerChangeListener() called"); + + // Remove the Java callbacks in the DiscoveryDelegateImpl Singleton + DiscoveryDelegateImpl * delegate = DiscoveryDelegateImpl::GetInstance(); + + // Check if the passed object is the same as the one added in addCastingPlayerChangeListener() JNI method + jboolean isSameObject = env->IsSameObject(castingPlayerChangeListenerJavaObject, delegate->castingPlayerChangeListenerJavaObject); + if ((bool)isSameObject) { + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::removeCastingPlayerChangeListener() received the same Listener jobject. Removing."); + + // Delete the global reference to the Java object + env->DeleteGlobalRef(delegate->castingPlayerChangeListenerJavaObject); + delegate->castingPlayerChangeListenerJavaObject = nullptr; + // No explicit cleanup required + delegate->onAddedCallbackJavaMethodID = nullptr; + delegate->onChangedCallbackJavaMethodID = nullptr; + //delegate->onRemovedCallbackJavaMethodID = nullptr; + + return support::createJMatterError(CHIP_NO_ERROR); + } else { + ChipLogError(AppServer, "CastingPlayerDiscovery-JNI::removeCastingPlayerChangeListener() Warning: Cannot remove. Received a diffrent CastingPlayerChangeListener object"); + + return support::createJMatterError(CHIP_ERROR_INCORRECT_STATE); + } +} + +// CHANGE NAME +JNI_METHOD(jobject, getDiscoveredCastingPlayers)(JNIEnv * env, jobject) +{ + chip::DeviceLayer::StackLock lock; + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::getDiscoveredCastingPlayers() called"); + + // Create a new ArrayList + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "", "()V"); + jobject arrayList = env->NewObject(arrayListClass, arrayListConstructor); + jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + + std::vector> castingPlayersList = CastingPlayerDiscovery::GetInstance()->GetCastingPlayers(); + + for (const auto& player : castingPlayersList) { + jobject matterCastingPlayerJavaObject = support::createJCastingPlayer(player); + if (matterCastingPlayerJavaObject != nullptr){ + jboolean added = env->CallBooleanMethod(arrayList, addMethod, matterCastingPlayerJavaObject); + if ((bool)added) { + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::getDiscoveredCastingPlayers() added CastingPlayer with ID: %s", player->GetId()); + } else { + ChipLogError(AppServer, "CastingPlayerDiscovery-JNI::getDiscoveredCastingPlayers() Warning: Unable to add CastingPlayer with ID: %s", player->GetId()); + } + } + } + + return arrayList; +} + +}; // namespace core +}; // namespace casting +}; // namespace matter diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.h b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.h new file mode 100644 index 00000000000000..94fb568b813960 --- /dev/null +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.h @@ -0,0 +1,41 @@ +/* + * + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include + +namespace matter { +namespace casting { +namespace core { + +class CastingPlayerDiscoveryJNI +{ +public: +private: + friend CastingPlayerDiscoveryJNI & CastingAppJNIMgr(); + static CastingPlayerDiscoveryJNI sInstance; +}; + +inline class CastingPlayerDiscoveryJNI & CastingAppJNIMgr() +{ + return CastingPlayerDiscoveryJNI::sInstance; +} +}; // namespace core +}; // namespace casting +}; // namespace matter diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.cpp new file mode 100644 index 00000000000000..09fb1b2081356f --- /dev/null +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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. + * + */ + +#include "CastingPlayerConverter-JNI.h" +#include + +namespace matter { +namespace casting { +namespace support { + +using namespace chip; + +jobject createJCastingPlayer(matter::casting::memory::Strong player) +{ + ChipLogProgress(AppServer, "CastingPlayerConverter-JNI.createJCastingPlayer() called"); + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + // Get a reference to the MatterCastingPlayer Java class + jclass matterCastingPlayerJavaClass = env->FindClass("com/matter/casting/core/MatterCastingPlayer"); + if (matterCastingPlayerJavaClass == nullptr) { + ChipLogError(AppServer, "CastingPlayerConverter-JNI.createJCastingPlayer() could not locate MatterCastingPlayer Java class"); + return nullptr; + } + + // Get the constructor for the com/matter/casting/core/MatterCastingPlayer Java class + jmethodID constructor = env->GetMethodID(matterCastingPlayerJavaClass, "", "(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/util/List;IJJJ)V"); + if (constructor == nullptr) { + ChipLogError(AppServer, "CastingPlayerConverter-JNI.createJCastingPlayer() could not locate MatterCastingPlayer Java class constructor"); + return nullptr; + } + + // Convert the CastingPlayer fields to MatterCastingPlayer Java types + bool connected = false; + if (player->IsConnected()) {connected = true;} + jboolean connectedJavaBoolean = static_cast(connected); + jstring idJavaString = env->NewStringUTF(player->GetId()); + jstring deviceNameJavaString = env->NewStringUTF(player->GetDeviceName()); + jstring hostNameJavaString = env->NewStringUTF(player->GetHostName()); + jstring instanceNameJavaString = env->NewStringUTF(player->GetInstanceName()); + jint numIPsJavaInt = (jint)(player->GetNumIPs()); + + jobject ipAddressListJavaObject = nullptr; + const chip::Inet::IPAddress * ipAddresses = player->GetIPAddresses(); + if (ipAddresses != nullptr) + { + chip::JniReferences::GetInstance().CreateArrayList(ipAddressListJavaObject); + for (size_t i = 0; i < player->GetNumIPs() && i < chip::Dnssd::CommonResolutionData::kMaxIPAddresses; i++) + { + char addrCString[chip::Inet::IPAddress::kMaxStringLength]; + ipAddresses[i].ToString(addrCString, chip::Inet::IPAddress::kMaxStringLength); + jstring jIPAddressStr = env->NewStringUTF(addrCString); + + jclass jIPAddressClass = env->FindClass("java/net/InetAddress"); + jmethodID jGetByNameMid = env->GetStaticMethodID(jIPAddressClass, "getByName", "(Ljava/lang/String;)Ljava/net/InetAddress;"); + jobject jIPAddress = env->CallStaticObjectMethod(jIPAddressClass, jGetByNameMid, jIPAddressStr); + + chip::JniReferences::GetInstance().AddToList(ipAddressListJavaObject, jIPAddress); + } + } + + // TODO: make sure Java to Native dosn't truncate values + // change to ints + jint portJavaInt = (jint)(player->GetPort()); + jlong productIdJavaLong = static_cast(player->GetProductId()); + jlong vendorIdJavaLong = static_cast(player->GetVendorId()); + jlong deviceTypeJavaLong = static_cast(player->GetDeviceType()); + + // Create a new instance of the MatterCastingPlayer Java class + jobject matterCastingPlayerJavaObject = nullptr; + matterCastingPlayerJavaObject = env->NewObject(matterCastingPlayerJavaClass, constructor, connectedJavaBoolean, idJavaString, deviceNameJavaString, hostNameJavaString, instanceNameJavaString, numIPsJavaInt, ipAddressListJavaObject, portJavaInt, productIdJavaLong, vendorIdJavaLong, deviceTypeJavaLong); + if (matterCastingPlayerJavaObject == nullptr){ + ChipLogError(AppServer, "CastingPlayerConverter-JNI.createJCastingPlayer() Warning: Could not create MatterCastingPlayer Java object"); + } + return matterCastingPlayerJavaObject; +} + +}; // namespace support +}; // namespace casting +}; // namespace matter \ No newline at end of file diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.h b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.h new file mode 100644 index 00000000000000..4a2d01d5db7a5b --- /dev/null +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.h @@ -0,0 +1,41 @@ +/* + * + * Copyright (c) 2023-2024 Project CHIP Authors + * + * 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. + */ +#pragma once + +#include "core/CastingPlayer.h" + +#include + +#include + +namespace matter { +namespace casting { +namespace support { + +/** +* @brief Convertes a native CastingPlayer into a MatterCastingPlayer jobject +* +* @param CastingPlayer represents a Matter commissioner that is able to play media to a physical +* output or to a display screen which is part of the device. +* +* @return pointer to the CastingPlayer jobject if created successfully, nullptr otherwise. +*/ +jobject createJCastingPlayer(matter::casting::memory::Strong player); + +}; // namespace support +}; // namespace casting +}; // namespace matter \ No newline at end of file diff --git a/examples/tv-casting-app/android/BUILD.gn b/examples/tv-casting-app/android/BUILD.gn index ae349bce337efe..33d046d975ac4f 100644 --- a/examples/tv-casting-app/android/BUILD.gn +++ b/examples/tv-casting-app/android/BUILD.gn @@ -38,6 +38,10 @@ shared_library("jni") { sources += [ "App/app/src/main/jni/cpp/core/CastingApp-JNI.cpp", "App/app/src/main/jni/cpp/core/CastingApp-JNI.h", + "App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.cpp", + "App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.h", + "App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.cpp", + "App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.h", "App/app/src/main/jni/cpp/support/ErrorConverter-JNI.cpp", "App/app/src/main/jni/cpp/support/ErrorConverter-JNI.h", "App/app/src/main/jni/cpp/support/RotatingDeviceIdUniqueIdProvider-JNI.cpp", @@ -97,6 +101,10 @@ android_library("java") { sources += [ "App/app/src/main/jni/com/matter/casting/core/CastingApp.java", "App/app/src/main/jni/com/matter/casting/core/CastingAppState.java", + "App/app/src/main/jni/com/matter/casting/core/CastingPlayer.java", + "App/app/src/main/jni/com/matter/casting/core/CastingPlayerDiscovery.java", + "App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayer.java", + "App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayerDiscovery.java", "App/app/src/main/jni/com/matter/casting/support/AppParameters.java", "App/app/src/main/jni/com/matter/casting/support/CommissionableData.java", "App/app/src/main/jni/com/matter/casting/support/DACProvider.java", diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp b/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp index bc495a27fff870..90162c493cf932 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp +++ b/examples/tv-casting-app/tv-casting-common/core/CastingApp.cpp @@ -44,6 +44,7 @@ CastingApp * CastingApp::GetInstance() CHIP_ERROR CastingApp::Initialize(const AppParameters & appParameters) { + ChipLogProgress(Discovery, "CastingApp::Initialize() called"); VerifyOrReturnError(mState == CASTING_APP_UNINITIALIZED, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(appParameters.GetCommissionableDataProvider() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(appParameters.GetDeviceAttestationCredentialsProvider() != nullptr, CHIP_ERROR_INVALID_ARGUMENT); @@ -81,6 +82,7 @@ CHIP_ERROR CastingApp::Initialize(const AppParameters & appParameters) CHIP_ERROR CastingApp::Start() { + ChipLogProgress(Discovery, "CastingApp::Start() called"); VerifyOrReturnError(mState == CASTING_APP_NOT_RUNNING, CHIP_ERROR_INCORRECT_STATE); // start Matter server @@ -96,6 +98,7 @@ CHIP_ERROR CastingApp::Start() CHIP_ERROR CastingApp::PostStartRegistrations() { + ChipLogProgress(Discovery, "CastingApp::PostStartRegistrations() called"); VerifyOrReturnError(mState == CASTING_APP_NOT_RUNNING, CHIP_ERROR_INCORRECT_STATE); auto & server = chip::Server::GetInstance(); @@ -118,6 +121,7 @@ CHIP_ERROR CastingApp::PostStartRegistrations() CHIP_ERROR CastingApp::Stop() { + ChipLogProgress(Discovery, "CastingApp::Stop() called"); VerifyOrReturnError(mState == CASTING_APP_RUNNING, CHIP_ERROR_INCORRECT_STATE); // TODO: add logic to capture CastingPlayers that we are currently connected to, so we can automatically reconnect with them on diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingPlayerDiscovery.cpp b/examples/tv-casting-app/tv-casting-common/core/CastingPlayerDiscovery.cpp index e391116c0ef1f7..422d17def520a7 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingPlayerDiscovery.cpp +++ b/examples/tv-casting-app/tv-casting-common/core/CastingPlayerDiscovery.cpp @@ -33,6 +33,7 @@ CastingPlayerDiscovery::CastingPlayerDiscovery() {} CastingPlayerDiscovery * CastingPlayerDiscovery::GetInstance() { + ChipLogProgress(Discovery, "CastingPlayerDiscovery::GetInstance() called"); if (_castingPlayerDiscovery == nullptr) { _castingPlayerDiscovery = new CastingPlayerDiscovery(); @@ -42,7 +43,7 @@ CastingPlayerDiscovery * CastingPlayerDiscovery::GetInstance() CHIP_ERROR CastingPlayerDiscovery::StartDiscovery(uint64_t deviceTypeFilter) { - ChipLogProgress(Discovery, "CastingPlayerDiscovery::StartDiscovery called"); + ChipLogProgress(Discovery, "CastingPlayerDiscovery::StartDiscovery() called"); VerifyOrReturnError(mState == DISCOVERY_READY, CHIP_ERROR_INCORRECT_STATE); mCommissionableNodeController.RegisterDeviceDiscoveryDelegate(&mDelegate); @@ -63,9 +64,11 @@ CHIP_ERROR CastingPlayerDiscovery::StartDiscovery(uint64_t deviceTypeFilter) CHIP_ERROR CastingPlayerDiscovery::StopDiscovery() { + ChipLogProgress(Discovery, "CastingPlayerDiscovery::StopDiscovery() called"); VerifyOrReturnError(mState == DISCOVERY_RUNNING, CHIP_ERROR_INCORRECT_STATE); ReturnErrorOnFailure(mCommissionableNodeController.StopDiscovery()); + mCastingPlayers.clear(); mState = DISCOVERY_READY; return CHIP_NO_ERROR; @@ -73,7 +76,7 @@ CHIP_ERROR CastingPlayerDiscovery::StopDiscovery() void DeviceDiscoveryDelegateImpl::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData & nodeData) { - ChipLogProgress(Discovery, "DeviceDiscoveryDelegateImpl::OnDiscoveredDevice called"); + ChipLogProgress(Discovery, "DeviceDiscoveryDelegateImpl::OnDiscoveredDevice() called"); VerifyOrReturn(mClientDelegate != nullptr, ChipLogError(NotSpecified, "CastingPlayerDeviceDiscoveryDelegate, mClientDelegate is a nullptr")); diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingPlayerDiscovery.h b/examples/tv-casting-app/tv-casting-common/core/CastingPlayerDiscovery.h index 2fd156267e4d24..96609eee697d09 100644 --- a/examples/tv-casting-app/tv-casting-common/core/CastingPlayerDiscovery.h +++ b/examples/tv-casting-app/tv-casting-common/core/CastingPlayerDiscovery.h @@ -112,6 +112,7 @@ class CastingPlayerDiscovery void SetDelegate(DiscoveryDelegate * clientDelegate) { + ChipLogProgress(Discovery, "CastingPlayerDiscovery::SetDelegate() called"); if (clientDelegate == nullptr) { mState = DISCOVERY_NOT_READY;