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 dc491e58b59170..ddc613658d1366 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 @@ -133,7 +133,9 @@ public String getCommissionerButtonText(DiscoveredNodeData commissioner) { ? (aux.isEmpty() ? "" : " from ") + "Vendor ID: " + commissioner.getVendorId() : ""; aux = aux.isEmpty() ? aux : "\n[" + aux + "]"; - return main + aux; + + String preCommissioned = commissioner.isPreCommissioned() ? " (Pre-commissioned)" : ""; + return main + aux + preCommissioned; } /** Interface for notifying the host. */ diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CommissioningFragment.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CommissioningFragment.java deleted file mode 100644 index ca01c26219cbf1..00000000000000 --- a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/CommissioningFragment.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.chip.casting.app; - -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import com.chip.casting.ContentApp; -import com.chip.casting.DiscoveredNodeData; -import com.chip.casting.FailureCallback; -import com.chip.casting.MatterCallbackHandler; -import com.chip.casting.MatterError; -import com.chip.casting.SuccessCallback; -import com.chip.casting.TvCastingApp; -import com.chip.casting.VideoPlayer; -import com.chip.casting.util.GlobalCastingConstants; - -/** A {@link Fragment} to get the TV Casting App commissioned. */ -public class CommissioningFragment extends Fragment { - private static final String TAG = CommissioningFragment.class.getSimpleName(); - - private final TvCastingApp tvCastingApp; - private final DiscoveredNodeData selectedCommissioner; - - private boolean openCommissioningWindowSuccess; - private boolean sendUdcSuccess; - - public CommissioningFragment(TvCastingApp tvCastingApp, DiscoveredNodeData selectedCommissioner) { - this.tvCastingApp = tvCastingApp; - this.selectedCommissioner = selectedCommissioner; - } - - /** - * Use this factory method to create a new instance of this fragment using the provided - * parameters. - * - * @return A new instance of fragment CommissioningFragment. - */ - public static CommissioningFragment newInstance( - TvCastingApp tvCastingApp, DiscoveredNodeData selectedCommissioner) { - return new CommissioningFragment(tvCastingApp, selectedCommissioner); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Callback callback = (CommissioningFragment.Callback) this.getActivity(); - this.openCommissioningWindowSuccess = - tvCastingApp.openBasicCommissioningWindow( - GlobalCastingConstants.CommissioningWindowDurationSecs, - new MatterCallbackHandler() { - @Override - public void handle(MatterError error) { - Log.d(TAG, "handle() called on CommissioningComplete event with " + error); - } - }, - new SuccessCallback() { - @Override - public void handle(VideoPlayer videoPlayer) { - Log.d(TAG, "handle() called on OnConnectionSuccess with " + videoPlayer); - callback.handleCommissioningComplete(); - } - }, - new FailureCallback() { - @Override - public void handle(MatterError matterError) { - Log.d(TAG, "handle() called on OnConnectionFailure with " + matterError); - } - }, - new SuccessCallback() { - @Override - public void handle(ContentApp contentApp) { - Log.d(TAG, "handle() called on OnNewOrUpdatedEndpoint with " + contentApp); - } - }); - if (this.openCommissioningWindowSuccess) { - if (selectedCommissioner != null && selectedCommissioner.getNumIPs() > 0) { - String ipAddress = selectedCommissioner.getIpAddresses().get(0).getHostAddress(); - Log.d( - TAG, - "CommissioningFragment calling tvCastingApp.sendUserDirectedCommissioningRequest with IP: " - + ipAddress - + " port: " - + selectedCommissioner.getPort()); - - this.sendUdcSuccess = tvCastingApp.sendCommissioningRequest(selectedCommissioner); - } - } - - return inflater.inflate(R.layout.fragment_commissioning, container, false); - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - String commissioningWindowStatus = "Failed to open commissioning window"; - if (this.openCommissioningWindowSuccess) { - commissioningWindowStatus = "Commissioning window has been opened. Commission manually."; - if (this.sendUdcSuccess) { - commissioningWindowStatus = - "Commissioning window has been opened. Commissioning requested from " - + selectedCommissioner.getDeviceName(); - } - TextView onboardingPayloadView = getView().findViewById(R.id.onboardingPayload); - onboardingPayloadView.setText( - "Onboarding PIN: " - + GlobalCastingConstants.SetupPasscode - + "\nDiscriminator: " - + GlobalCastingConstants.Discriminator); - } - - TextView commissioningWindowStatusView = getView().findViewById(R.id.commissioningWindowStatus); - commissioningWindowStatusView.setText(commissioningWindowStatus); - } - - /** Interface for notifying the host. */ - public interface Callback { - /** Notifies listener to trigger transition on completion of commissioning */ - void handleCommissioningComplete(); - } -} diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ConnectionFragment.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ConnectionFragment.java new file mode 100644 index 00000000000000..c48b28974ef22e --- /dev/null +++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ConnectionFragment.java @@ -0,0 +1,155 @@ +package com.chip.casting.app; + +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import com.chip.casting.ContentApp; +import com.chip.casting.DiscoveredNodeData; +import com.chip.casting.FailureCallback; +import com.chip.casting.MatterCallbackHandler; +import com.chip.casting.MatterError; +import com.chip.casting.SuccessCallback; +import com.chip.casting.TvCastingApp; +import com.chip.casting.VideoPlayer; +import com.chip.casting.util.GlobalCastingConstants; + +/** A {@link Fragment} to get the TV Casting App commissioned / connected. */ +public class ConnectionFragment extends Fragment { + private static final String TAG = ConnectionFragment.class.getSimpleName(); + + private final TvCastingApp tvCastingApp; + private final DiscoveredNodeData selectedCommissioner; + + private boolean verifyOrEstablishConnectionSuccess; + private boolean openCommissioningWindowSuccess; + private boolean sendUdcSuccess; + + public ConnectionFragment(TvCastingApp tvCastingApp, DiscoveredNodeData selectedCommissioner) { + this.tvCastingApp = tvCastingApp; + this.selectedCommissioner = selectedCommissioner; + } + + /** + * Use this factory method to create a new instance of this fragment using the provided + * parameters. + * + * @return A new instance of fragment CommissioningFragment. + */ + public static ConnectionFragment newInstance( + TvCastingApp tvCastingApp, DiscoveredNodeData selectedCommissioner) { + return new ConnectionFragment(tvCastingApp, selectedCommissioner); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + Callback callback = (ConnectionFragment.Callback) this.getActivity(); + + SuccessCallback onConnectionSuccess = + new SuccessCallback() { + @Override + public void handle(VideoPlayer videoPlayer) { + Log.d(TAG, "handle() called on OnConnectionSuccess with " + videoPlayer); + callback.handleCommissioningComplete(); + } + }; + + FailureCallback onConnectionFailure = + new FailureCallback() { + @Override + public void handle(MatterError matterError) { + Log.d(TAG, "handle() called on OnConnectionFailure with " + matterError); + } + }; + + SuccessCallback onNewOrUpdatedEndpoints = + new SuccessCallback() { + @Override + public void handle(ContentApp contentApp) { + Log.d(TAG, "handle() called on OnNewOrUpdatedEndpoint with " + contentApp); + } + }; + + if (selectedCommissioner != null && selectedCommissioner.isPreCommissioned()) { + VideoPlayer videoPlayer = selectedCommissioner.toConnectableVideoPlayer(); + Log.d(TAG, "Calling verifyOrEstablishConnectionSuccess with VideoPlayer: " + videoPlayer); + this.verifyOrEstablishConnectionSuccess = + tvCastingApp.verifyOrEstablishConnection( + videoPlayer, onConnectionSuccess, onConnectionFailure, onNewOrUpdatedEndpoints); + } else { + Log.d(TAG, "Running commissioning"); + this.openCommissioningWindowSuccess = + tvCastingApp.openBasicCommissioningWindow( + GlobalCastingConstants.CommissioningWindowDurationSecs, + new MatterCallbackHandler() { + @Override + public void handle(MatterError error) { + Log.d(TAG, "handle() called on CommissioningComplete event with " + error); + } + }, + onConnectionSuccess, + onConnectionFailure, + onNewOrUpdatedEndpoints); + + if (this.openCommissioningWindowSuccess) { + if (selectedCommissioner != null && selectedCommissioner.getNumIPs() > 0) { + String ipAddress = selectedCommissioner.getIpAddresses().get(0).getHostAddress(); + Log.d( + TAG, + "ConnectionFragment calling tvCastingApp.sendUserDirectedCommissioningRequest with IP: " + + ipAddress + + " port: " + + selectedCommissioner.getPort()); + + this.sendUdcSuccess = tvCastingApp.sendCommissioningRequest(selectedCommissioner); + } + } + } + + return inflater.inflate(R.layout.fragment_connection, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + String commissioningWindowStatus = ""; + if (selectedCommissioner != null && selectedCommissioner.isPreCommissioned()) { + commissioningWindowStatus = "Establishing connection with selected Video Player"; + } else { + commissioningWindowStatus = "Failed to open commissioning window"; + if (this.openCommissioningWindowSuccess) { + commissioningWindowStatus = "Commissioning window has been opened. Commission manually."; + if (this.sendUdcSuccess) { + commissioningWindowStatus = + "Commissioning window has been opened. Commissioning requested from " + + selectedCommissioner.getDeviceName(); + } + TextView onboardingPayloadView = getView().findViewById(R.id.onboardingPayload); + onboardingPayloadView.setText( + "Onboarding PIN: " + + GlobalCastingConstants.SetupPasscode + + "\nDiscriminator: " + + GlobalCastingConstants.Discriminator); + } + } + + TextView commissioningWindowStatusView = getView().findViewById(R.id.commissioningWindowStatus); + commissioningWindowStatusView.setText(commissioningWindowStatus); + } + + /** Interface for notifying the host. */ + public interface Callback { + /** Notifies listener to trigger transition on completion of commissioning */ + void handleCommissioningComplete(); + } +} 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 88b85610b475f6..d2a4327685b832 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 @@ -15,14 +15,16 @@ import chip.platform.NsdManagerServiceResolver; import chip.platform.PreferencesConfigurationManager; import chip.platform.PreferencesKeyValueStoreManager; +import com.chip.casting.AppParameters; import com.chip.casting.DACProviderStub; import com.chip.casting.DiscoveredNodeData; import com.chip.casting.TvCastingApp; import com.chip.casting.util.GlobalCastingConstants; +import java.util.Random; public class MainActivity extends AppCompatActivity implements CommissionerDiscoveryFragment.Callback, - CommissioningFragment.Callback, + ConnectionFragment.Callback, SelectClusterFragment.Callback { private static final String TAG = MainActivity.class.getSimpleName(); @@ -46,7 +48,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void handleCommissioningButtonClicked(DiscoveredNodeData commissioner) { - showFragment(CommissioningFragment.newInstance(tvCastingApp, commissioner)); + showFragment(ConnectionFragment.newInstance(tvCastingApp, commissioner)); } @Override @@ -90,7 +92,12 @@ private void initJni() { chipAppServer = new ChipAppServer(); chipAppServer.startApp(); - tvCastingApp.init(); + AppParameters appParameters = new AppParameters(); + byte[] rotatingDeviceIdUniqueId = + new byte[AppParameters.MIN_ROTATING_DEVICE_ID_UNIQUE_ID_LENGTH]; + new Random().nextBytes(rotatingDeviceIdUniqueId); + appParameters.setRotatingDeviceIdUniqueId(rotatingDeviceIdUniqueId); + tvCastingApp.init(appParameters); } private void showFragment(Fragment fragment, boolean showOnBack) { diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/AppParameters.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/AppParameters.java new file mode 100644 index 00000000000000..ffca2ac246dd40 --- /dev/null +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/chip/casting/AppParameters.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 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.chip.casting; + +public class AppParameters { + public static final int MIN_ROTATING_DEVICE_ID_UNIQUE_ID_LENGTH = 16; + + private byte[] rotatingDeviceIdUniqueId; + + public void setRotatingDeviceIdUniqueId(byte[] rotatingDeviceIdUniqueId) { + this.rotatingDeviceIdUniqueId = rotatingDeviceIdUniqueId; + } + + public byte[] getRotatingDeviceIdUniqueId() { + return rotatingDeviceIdUniqueId; + } +} 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 982fbd84800671..b8a051036edd54 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 @@ -47,6 +47,8 @@ public class DiscoveredNodeData { private int numIPs; private List ipAddresses; + private VideoPlayer connectableVideoPlayer; + public DiscoveredNodeData(NsdServiceInfo serviceInfo) { Map attributes = serviceInfo.getAttributes(); this.deviceName = new String(attributes.get(KEY_DEVICE_NAME), StandardCharsets.UTF_8); @@ -69,6 +71,18 @@ public DiscoveredNodeData(NsdServiceInfo serviceInfo) { this.numIPs = 1; } + void setConnectableVideoPlayer(VideoPlayer videoPlayer) { + this.connectableVideoPlayer = videoPlayer; + } + + public boolean isPreCommissioned() { + return connectableVideoPlayer != null; + } + + public VideoPlayer toConnectableVideoPlayer() { + return connectableVideoPlayer; + } + public String getHostName() { return hostName; } 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 cbc2ccadbad571..5a0f48550634f0 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 @@ -28,6 +28,7 @@ public class NsdDiscoveryListener implements NsdManager.DiscoveryListener { private final NsdManager nsdManager; private final String targetServiceType; private final List deviceTypeFilter; + private final List preCommissionedVideoPlayers; private final SuccessCallback successCallback; private final FailureCallback failureCallback; @@ -35,11 +36,13 @@ public NsdDiscoveryListener( NsdManager nsdManager, String targetServiceType, List deviceTypeFilter, + List preCommissionedVideoPlayers, SuccessCallback successCallback, FailureCallback failureCallback) { this.nsdManager = nsdManager; this.targetServiceType = targetServiceType; this.deviceTypeFilter = deviceTypeFilter; + this.preCommissionedVideoPlayers = preCommissionedVideoPlayers; this.successCallback = successCallback; this.failureCallback = failureCallback; } @@ -55,7 +58,12 @@ public void onServiceFound(NsdServiceInfo service) { if (service.getServiceType().equals(targetServiceType)) { nsdManager.resolveService( service, - new NsdResolveListener(nsdManager, deviceTypeFilter, successCallback, failureCallback)); + new NsdResolveListener( + nsdManager, + deviceTypeFilter, + preCommissionedVideoPlayers, + successCallback, + failureCallback)); } else { Log.d(TAG, "Ignoring discovered service: " + service.toString()); } 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 6275ad2b491a29..55de0226489427 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 @@ -28,16 +28,24 @@ public class NsdResolveListener implements NsdManager.ResolveListener { private final NsdManager nsdManager; private final List deviceTypeFilter; + private final List preCommissionedVideoPlayers; private final SuccessCallback successCallback; private final FailureCallback failureCallback; public NsdResolveListener( NsdManager nsdManager, List deviceTypeFilter, + List preCommissionedVideoPlayers, SuccessCallback successCallback, FailureCallback failureCallback) { this.nsdManager = nsdManager; this.deviceTypeFilter = deviceTypeFilter; + this.preCommissionedVideoPlayers = preCommissionedVideoPlayers; + if (preCommissionedVideoPlayers != null) { + for (VideoPlayer videoPlayer : preCommissionedVideoPlayers) { + Log.d(TAG, "Precommissioned video player: " + videoPlayer); + } + } this.successCallback = successCallback; this.failureCallback = failureCallback; } @@ -48,6 +56,7 @@ public void onServiceResolved(NsdServiceInfo serviceInfo) { Log.d(TAG, "DiscoveredNodeData resolved: " + discoveredNodeData); if (isPassingDeviceTypeFilter(discoveredNodeData)) { + addCommissioningInfo(discoveredNodeData); successCallback.handle(discoveredNodeData); } else { Log.d( @@ -62,9 +71,9 @@ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { switch (errorCode) { case NsdManager.FAILURE_ALREADY_ACTIVE: Log.e(TAG, "NsdResolveListener FAILURE_ALREADY_ACTIVE - Service: " + serviceInfo); - nsdManager.resolveService( - serviceInfo, - new NsdResolveListener(nsdManager, deviceTypeFilter, successCallback, failureCallback)); + failureCallback.handle( + new MatterError( + 3, "NsdResolveListener FAILURE_ALREADY_ACTIVE - Service: " + serviceInfo)); break; case NsdManager.FAILURE_INTERNAL_ERROR: Log.e(TAG, "NsdResolveListener FAILURE_INTERNAL_ERROR - Service: " + serviceInfo); @@ -85,4 +94,23 @@ private boolean isPassingDeviceTypeFilter(DiscoveredNodeData discoveredNodeData) || deviceTypeFilter.isEmpty() || deviceTypeFilter.contains(discoveredNodeData.getDeviceType()); } + + private void addCommissioningInfo(DiscoveredNodeData discoveredNodeData) { + if (preCommissionedVideoPlayers != null) { + for (VideoPlayer videoPlayer : preCommissionedVideoPlayers) { + if (videoPlayer.isSameAs(discoveredNodeData)) { + Log.d( + TAG, + "Matching Video Player with the following information found for DiscoveredNodeData" + + videoPlayer); + discoveredNodeData.setConnectableVideoPlayer(videoPlayer); + return; + } + } + } + Log.d( + TAG, + "No matching VideoPlayers found from the cache for new DiscoveredNodeData: " + + 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 364b3a1502be0b..20235ae21b38e2 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 @@ -32,7 +32,7 @@ public class TvCastingApp { private final String TARGET_SERVICE_TYPE = "_matterd._udp."; private final List DEVICE_TYPE_FILTER = Arrays.asList(35L); // Video player = 35; - public native void init(); + public native boolean init(AppParameters appParameters); public native void setDACProvider(DACProvider provider); @@ -47,11 +47,14 @@ public void discoverVideoPlayerCommissioners( multicastLock.setReferenceCounted(true); multicastLock.acquire(); + List preCommissionedVideoPlayers = readCachedVideoPlayers(); + NsdDiscoveryListener nsdDiscoveryListener = new NsdDiscoveryListener( nsdManager, TARGET_SERVICE_TYPE, DEVICE_TYPE_FILTER, + preCommissionedVideoPlayers, discoverySuccessCallback, discoveryFailureCallback); @@ -63,6 +66,7 @@ public void discoverVideoPlayerCommissioners( new Runnable() { @Override public void run() { + Log.d(TAG, "TvCastingApp stopping Video Player commissioner discovery"); nsdManager.stopServiceDiscovery(nsdDiscoveryListener); multicastLock.release(); } @@ -275,7 +279,7 @@ public native boolean keypadInput_sendKey( /** * APPLICATION BASIC * - *

TODO: Add APIs to subscribe to Application, Status and AllowedVendorList + *

TODO: Add APIs to subscribe to & read Application, Status and AllowedVendorList */ public native boolean applicationBasic_subscribeToVendorName( ContentApp contentApp, @@ -287,7 +291,7 @@ public native boolean applicationBasic_subscribeToVendorName( public native boolean applicationBasic_subscribeToVendorID( ContentApp contentApp, - SuccessCallback readSuccessHandler, + SuccessCallback readSuccessHandler, FailureCallback readFailureHandler, int minInterval, int maxInterval, @@ -303,7 +307,7 @@ public native boolean applicationBasic_subscribeToApplicationName( public native boolean applicationBasic_subscribeToProductID( ContentApp contentApp, - SuccessCallback readSuccessHandler, + SuccessCallback readSuccessHandler, FailureCallback readFailureHandler, int minInterval, int maxInterval, @@ -317,6 +321,31 @@ public native boolean applicationBasic_subscribeToApplicationVersion( int maxInterval, SubscriptionEstablishedCallback subscriptionEstablishedHandler); + public native boolean applicationBasic_readVendorName( + ContentApp contentApp, + SuccessCallback readSuccessHandler, + FailureCallback readFailureHandler); + + public native boolean applicationBasic_readVendorID( + ContentApp contentApp, + SuccessCallback readSuccessHandler, + FailureCallback readFailureHandler); + + public native boolean applicationBasic_readApplicationName( + ContentApp contentApp, + SuccessCallback readSuccessHandler, + FailureCallback readFailureHandler); + + public native boolean applicationBasic_readProductID( + ContentApp contentApp, + SuccessCallback readSuccessHandler, + FailureCallback readFailureHandler); + + public native boolean applicationBasic_readApplicationVersion( + ContentApp contentApp, + SuccessCallback readSuccessHandler, + FailureCallback readFailureHandlerr); + static { System.loadLibrary("TvCastingApp"); } 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 85c487850cd08d..9ba557c910b704 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 @@ -17,10 +17,15 @@ */ package com.chip.casting; +import java.net.InetAddress; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; public class VideoPlayer { + private static final String TAG = VideoPlayer.class.getSimpleName(); + private long nodeId; private byte fabricIndex; private String deviceName; @@ -30,6 +35,9 @@ public class VideoPlayer { private List contentApps; private boolean isConnected = false; + private int numIPs; + private List ipAddresses; + private boolean isInitialized = false; public VideoPlayer( @@ -40,6 +48,8 @@ public VideoPlayer( int productId, int deviceType, List contentApps, + int numIPs, + List ipAddresses, boolean isConnected) { this.nodeId = nodeId; this.fabricIndex = fabricIndex; @@ -49,9 +59,43 @@ public VideoPlayer( this.deviceType = deviceType; this.contentApps = contentApps; this.isConnected = isConnected; + this.numIPs = numIPs; + this.ipAddresses = ipAddresses; this.isInitialized = true; } + public boolean isSameAs(DiscoveredNodeData discoveredNodeData) { + // return false because 'this' VideoPlayer is not null + if (discoveredNodeData == null) { + return false; + } + + // return false because deviceNames are different + if (Objects.equals(deviceName, discoveredNodeData.getDeviceName()) == false) { + return false; + } + + // return false because not even a single IP Address matches + if (ipAddresses != null) { + boolean matchFound = false; + Set discoveredNodeDataIpAddressSet = + new HashSet(discoveredNodeData.getIpAddresses()); + for (InetAddress videoPlayerIpAddress : ipAddresses) { + if (discoveredNodeDataIpAddressSet.contains(videoPlayerIpAddress)) { + matchFound = true; + break; + } + } + + if (!matchFound) { + return false; + } + } + + // otherwise, return true + return true; + } + public boolean equals(Object object) { if (this == object) return true; if (object == null || getClass() != object.getClass()) return false; @@ -78,8 +122,18 @@ public java.lang.String toString() { + vendorId + ", productId=" + productId + + ", deviceType=" + + deviceType + + ", contentApps=" + + contentApps + ", isConnected=" + isConnected + + ", numIPs=" + + numIPs + + ", ipAddresses=" + + ipAddresses + + ", isInitialized=" + + isInitialized + '}'; } diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/ConversionUtils.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/ConversionUtils.cpp index db59f4a08d1276..8112c6b2acfb90 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/ConversionUtils.cpp +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/ConversionUtils.cpp @@ -22,6 +22,27 @@ #include #include +CHIP_ERROR convertJAppParametersToCppAppParams(jobject appParameters, AppParams & outAppParams) +{ + ChipLogProgress(AppServer, "convertJContentAppToTargetEndpointInfo called"); + JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturnError(appParameters != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + jclass jAppParametersClass; + ReturnErrorOnFailure( + chip::JniReferences::GetInstance().GetClassRef(env, "com/chip/casting/AppParameters", jAppParametersClass)); + + jfieldID jRotatingDeviceIdUniqueIdField = env->GetFieldID(jAppParametersClass, "rotatingDeviceIdUniqueId", "[B"); + jobject jRotatingDeviceIdUniqueId = env->GetObjectField(appParameters, jRotatingDeviceIdUniqueIdField); + if (jRotatingDeviceIdUniqueId != nullptr) + { + chip::JniByteArray jniRotatingDeviceIdUniqueIdByteArray(env, static_cast(jRotatingDeviceIdUniqueId)); + outAppParams.SetRotatingDeviceIdUniqueId(MakeOptional(jniRotatingDeviceIdUniqueIdByteArray.byteSpan())); + } + + return CHIP_NO_ERROR; +} + CHIP_ERROR convertJContentAppToTargetEndpointInfo(jobject contentApp, TargetEndpointInfo & outTargetEndpointInfo) { ChipLogProgress(AppServer, "convertJContentAppToTargetEndpointInfo called"); @@ -99,14 +120,14 @@ CHIP_ERROR convertJVideoPlayerToTargetVideoPlayerInfo(jobject videoPlayer, Targe jfieldID jFabricIndexField = env->GetFieldID(jVideoPlayerClass, "fabricIndex", "B"); chip::FabricIndex fabricIndex = static_cast(env->GetByteField(videoPlayer, jFabricIndexField)); - jfieldID jVendorIdField = env->GetFieldID(jVideoPlayerClass, "vendorId", "S"); - uint16_t vendorId = static_cast(env->GetShortField(videoPlayer, jVendorIdField)); + jfieldID jVendorIdField = env->GetFieldID(jVideoPlayerClass, "vendorId", "I"); + uint16_t vendorId = static_cast(env->GetIntField(videoPlayer, jVendorIdField)); - jfieldID jProductIdField = env->GetFieldID(jVideoPlayerClass, "productId", "S"); - uint16_t productId = static_cast(env->GetShortField(videoPlayer, jProductIdField)); + jfieldID jProductIdField = env->GetFieldID(jVideoPlayerClass, "productId", "I"); + uint16_t productId = static_cast(env->GetIntField(videoPlayer, jProductIdField)); - jfieldID jDeviceType = env->GetFieldID(jVideoPlayerClass, "deviceType", "S"); - uint16_t deviceType = static_cast(env->GetShortField(videoPlayer, jDeviceType)); + jfieldID jDeviceType = env->GetFieldID(jVideoPlayerClass, "deviceType", "I"); + uint16_t deviceType = static_cast(env->GetIntField(videoPlayer, jDeviceType)); jfieldID getDeviceNameField = env->GetFieldID(jVideoPlayerClass, "deviceName", "Ljava/lang/String;"); jstring jDeviceName = static_cast(env->GetObjectField(videoPlayer, getDeviceNameField)); @@ -152,7 +173,7 @@ CHIP_ERROR convertTargetVideoPlayerInfoToJVideoPlayer(TargetVideoPlayerInfo * ta ReturnErrorOnFailure( chip::JniReferences::GetInstance().GetClassRef(env, "com/chip/casting/VideoPlayer", jVideoPlayerClass)); jmethodID jVideoPlayerConstructor = - env->GetMethodID(jVideoPlayerClass, "", "(JBLjava/lang/String;IIILjava/util/List;Z)V"); + env->GetMethodID(jVideoPlayerClass, "", "(JBLjava/lang/String;IIILjava/util/List;ILjava/util/List;Z)V"); jobject jContentAppList = nullptr; TargetEndpointInfo * endpoints = targetVideoPlayerInfo->GetEndpoints(); @@ -166,11 +187,37 @@ CHIP_ERROR convertTargetVideoPlayerInfoToJVideoPlayer(TargetVideoPlayerInfo * ta chip::JniReferences::GetInstance().AddToList(jContentAppList, contentApp); } } - jstring deviceName = env->NewStringUTF(targetVideoPlayerInfo->GetDeviceName()); - outVideoPlayer = env->NewObject(jVideoPlayerClass, jVideoPlayerConstructor, targetVideoPlayerInfo->GetNodeId(), + + jstring deviceName = + targetVideoPlayerInfo->GetDeviceName() == nullptr ? nullptr : env->NewStringUTF(targetVideoPlayerInfo->GetDeviceName()); + + jobject jIPAddressList = nullptr; + const chip::Inet::IPAddress * ipAddresses = targetVideoPlayerInfo->GetIpAddresses(); + if (ipAddresses != nullptr) + { + chip::JniReferences::GetInstance().CreateArrayList(jIPAddressList); + for (size_t i = 0; i < targetVideoPlayerInfo->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; + ReturnErrorOnFailure(chip::JniReferences::GetInstance().GetClassRef(env, "java/net/InetAddress", jIPAddressClass)); + jmethodID jGetByNameMid = + env->GetStaticMethodID(jIPAddressClass, "getByName", "(Ljava/lang/String;)Ljava/net/InetAddress;"); + jobject jIPAddress = env->CallStaticObjectMethod(jIPAddressClass, jGetByNameMid, jIPAddressStr); + + chip::JniReferences::GetInstance().AddToList(jIPAddressList, jIPAddress); + } + } + + outVideoPlayer = env->NewObject(jVideoPlayerClass, jVideoPlayerConstructor, targetVideoPlayerInfo->GetNodeId(), targetVideoPlayerInfo->GetFabricIndex(), deviceName, targetVideoPlayerInfo->GetVendorId(), targetVideoPlayerInfo->GetProductId(), targetVideoPlayerInfo->GetDeviceType(), - jContentAppList, targetVideoPlayerInfo->GetOperationalDeviceProxy() != nullptr); + jContentAppList, targetVideoPlayerInfo->GetNumIPs(), jIPAddressList, + targetVideoPlayerInfo->GetOperationalDeviceProxy() != nullptr); } return CHIP_NO_ERROR; } diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/ConversionUtils.h b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/ConversionUtils.h index 192ffa2b65961d..a2c59596d9e9e5 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/ConversionUtils.h +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/ConversionUtils.h @@ -16,11 +16,14 @@ */ #pragma once +#include "AppParams.h" #include "TargetEndpointInfo.h" #include "TargetVideoPlayerInfo.h" #include +CHIP_ERROR convertJAppParametersToCppAppParams(jobject appParameters, AppParams & outAppParams); + CHIP_ERROR convertJContentAppToTargetEndpointInfo(jobject contentApp, TargetEndpointInfo & outTargetEndpointInfo); CHIP_ERROR convertTargetEndpointInfoToJContentApp(TargetEndpointInfo * targetEndpointInfo, jobject & outContentApp); diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/MatterCallbackHandler-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/MatterCallbackHandler-JNI.cpp index 60a10ea2696f5e..862334ae1ee4cb 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/MatterCallbackHandler-JNI.cpp +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/MatterCallbackHandler-JNI.cpp @@ -178,7 +178,7 @@ jobject OnConnectionSuccessHandlerJNI::ConvertToJObject(TargetVideoPlayerInfo * { ChipLogError(AppServer, "OnConnectionSuccessHandlerJNI::ConvertToJObject failed with %" CHIP_ERROR_FORMAT, err.Format()); } - return nullptr; + return videoPlayer; } jobject OnNewOrUpdatedEndpointHandlerJNI::ConvertToJObject(TargetEndpointInfo * targetEndpointInfo) @@ -383,7 +383,7 @@ jobject VendorIDSuccessHandlerJNI::ConvertToJObject( chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType responseData) { ChipLogProgress(AppServer, "VendorIDSuccessHandlerJNI::ConvertToJObject called"); - return ConvertToShortJObject(responseData); + return ConvertToIntegerJObject(responseData); } jobject ApplicationNameSuccessHandlerJNI::ConvertToJObject( @@ -397,7 +397,7 @@ jobject ProductIDSuccessHandlerJNI::ConvertToJObject( chip::app::Clusters::ApplicationBasic::Attributes::ProductID::TypeInfo::DecodableArgType responseData) { ChipLogProgress(AppServer, "ProductIDSuccessHandlerJNI::ConvertToJObject called"); - return ConvertToShortJObject(responseData); + return ConvertToIntegerJObject(responseData); } jobject ApplicationVersionSuccessHandlerJNI::ConvertToJObject( diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/MatterCallbackHandler-JNI.h b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/MatterCallbackHandler-JNI.h index d78988699b88f9..3261aa60874ad2 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/MatterCallbackHandler-JNI.h +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/MatterCallbackHandler-JNI.h @@ -235,7 +235,7 @@ class VendorIDSuccessHandlerJNI : public SuccessHandlerJNI { public: - VendorIDSuccessHandlerJNI() : SuccessHandlerJNI("(Ljava/lang/Short;)V") {} + VendorIDSuccessHandlerJNI() : SuccessHandlerJNI("(Ljava/lang/Integer;)V") {} jobject ConvertToJObject(chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType responseData); }; @@ -252,7 +252,7 @@ class ProductIDSuccessHandlerJNI : public SuccessHandlerJNI { public: - ProductIDSuccessHandlerJNI() : SuccessHandlerJNI("(Ljava/lang/Short;)V") {} + ProductIDSuccessHandlerJNI() : SuccessHandlerJNI("(Ljava/lang/Integer;)V") {} jobject ConvertToJObject(chip::app::Clusters::ApplicationBasic::Attributes::ProductID::TypeInfo::DecodableArgType responseData); }; diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/TvCastingApp-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/TvCastingApp-JNI.cpp index 928f489e2b84a7..33d7c753bf9042 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/TvCastingApp-JNI.cpp +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/TvCastingApp-JNI.cpp @@ -52,6 +52,36 @@ void JNI_OnUnload(JavaVM * jvm, void * reserved) return AndroidAppServerJNI_OnUnload(jvm, reserved); } +JNI_METHOD(jboolean, init)(JNIEnv *, jobject, jobject jAppParameters) +{ + chip::DeviceLayer::StackLock lock; + ChipLogProgress(AppServer, "JNI_METHOD init called"); + + CHIP_ERROR err = CHIP_NO_ERROR; + if (jAppParameters == nullptr) + { + err = CastingServer::GetInstance()->Init(); + } + else + { + AppParams appParams; + err = convertJAppParametersToCppAppParams(jAppParameters, appParams); + VerifyOrExit(err == CHIP_NO_ERROR, + ChipLogError(AppServer, "Conversion of AppParameters from jobject to Cpp type failed: %" CHIP_ERROR_FORMAT, + err.Format())); + err = CastingServer::GetInstance()->Init(&appParams); + } + VerifyOrExit(err == CHIP_NO_ERROR, + ChipLogError(AppServer, "Call to CastingServer::GetInstance()->Init() failed: %" CHIP_ERROR_FORMAT, err.Format())); +exit: + if (err != CHIP_NO_ERROR) + { + return false; + } + + return true; +} + JNI_METHOD(void, setDACProvider)(JNIEnv *, jobject, jobject provider) { if (!chip::Credentials::IsDeviceAttestationCredentialsProviderSet()) @@ -202,6 +232,7 @@ JNI_METHOD(jobject, getActiveTargetVideoPlayers)(JNIEnv * env, jobject) JNI_METHOD(jboolean, sendUserDirectedCommissioningRequest)(JNIEnv * env, jobject, jstring addressJStr, jint port) { + chip::DeviceLayer::StackLock lock; ChipLogProgress(AppServer, "JNI_METHOD sendUserDirectedCommissioningRequest called with port %d", port); Inet::IPAddress addressInet; JniUtfString addressJniString(env, addressJStr); @@ -225,6 +256,7 @@ JNI_METHOD(jboolean, sendUserDirectedCommissioningRequest)(JNIEnv * env, jobject JNI_METHOD(jboolean, sendCommissioningRequest)(JNIEnv * env, jobject, jobject jDiscoveredNodeData) { + chip::DeviceLayer::StackLock lock; ChipLogProgress(AppServer, "JNI_METHOD sendCommissioningRequest called"); chip::Dnssd::DiscoveredNodeData commissioner; @@ -245,12 +277,6 @@ JNI_METHOD(jboolean, sendCommissioningRequest)(JNIEnv * env, jobject, jobject jD return true; } -JNI_METHOD(void, init)(JNIEnv *, jobject) -{ - ChipLogProgress(AppServer, "JNI_METHOD init called"); - CastingServer::GetInstance()->Init(); -} - JNI_METHOD(jboolean, contentLauncherLaunchURL) (JNIEnv * env, jobject, jobject contentApp, jstring contentUrl, jstring contentDisplayStr, jobject jResponseHandler) { @@ -411,9 +437,7 @@ JNI_METHOD(jboolean, contentLauncher_1subscribeToSupportedStreamingProtocols) err = TvCastingAppJNIMgr().getSupportedStreamingProtocolsSuccessHandler().SetUp(env, jReadSuccessHandler); VerifyOrExit(CHIP_NO_ERROR == err, ChipLogError(AppServer, "SuccessHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); - err = TvCastingAppJNIMgr() - .getSubscriptionReadFailureHandler(ContentLauncher_SupportedStreamingProtocols) - .SetUp(env, jReadFailureHandler); + err = TvCastingAppJNIMgr().getReadFailureHandler(ContentLauncher_SupportedStreamingProtocols).SetUp(env, jReadFailureHandler); VerifyOrExit(CHIP_NO_ERROR == err, ChipLogError(AppServer, "SubscriptionReadFailureHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); @@ -430,7 +454,7 @@ JNI_METHOD(jboolean, contentLauncher_1subscribeToSupportedStreamingProtocols) TvCastingAppJNIMgr().getSupportedStreamingProtocolsSuccessHandler().Handle(responseData); }, [](void * context, CHIP_ERROR err) { - TvCastingAppJNIMgr().getSubscriptionReadFailureHandler(ContentLauncher_SupportedStreamingProtocols).Handle(err); + TvCastingAppJNIMgr().getReadFailureHandler(ContentLauncher_SupportedStreamingProtocols).Handle(err); }, static_cast(minInterval), static_cast(maxInterval), [](void * context) { @@ -1742,3 +1766,209 @@ JNI_METHOD(jboolean, applicationBasic_1subscribeToApplicationVersion) return true; } + +JNI_METHOD(jboolean, applicationBasic_1readVendorName) +(JNIEnv * env, jobject, jobject contentApp, jobject jReadSuccessHandler, jobject jReadFailureHandler) +{ + chip::DeviceLayer::StackLock lock; + + ChipLogProgress(AppServer, "JNI_METHOD applicationBasic_1readVendorName called"); + + TargetEndpointInfo endpoint; + CHIP_ERROR err = convertJContentAppToTargetEndpointInfo(contentApp, endpoint); + VerifyOrExit(err == CHIP_NO_ERROR, + ChipLogError(AppServer, "Conversion from jobject contentApp to TargetEndpointInfo * failed: %" CHIP_ERROR_FORMAT, + err.Format())); + + err = TvCastingAppJNIMgr().getReadVendorNameSuccessHandler().SetUp(env, jReadSuccessHandler); + VerifyOrExit(CHIP_NO_ERROR == err, ChipLogError(AppServer, "SuccessHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_VendorName).SetUp(env, jReadFailureHandler); + VerifyOrExit(CHIP_NO_ERROR == err, + ChipLogError(AppServer, "ReadFailureHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = CastingServer::GetInstance()->ApplicationBasic_ReadVendorName( + &endpoint, nullptr, + [](void * context, chip::app::Clusters::ApplicationBasic::Attributes::VendorName::TypeInfo::DecodableArgType responseData) { + TvCastingAppJNIMgr().getVendorNameSuccessHandler().Handle(responseData); + }, + [](void * context, CHIP_ERROR err) { + TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_VendorName).Handle(err); + }); + + VerifyOrExit( + CHIP_NO_ERROR == err, + ChipLogError(AppServer, "CastingServer.applicationBasic_1readVendorName failed %" CHIP_ERROR_FORMAT, err.Format())); + +exit: + if (err != CHIP_NO_ERROR) + { + return false; + } + + return true; +} + +JNI_METHOD(jboolean, applicationBasic_1readVendorID) +(JNIEnv * env, jobject, jobject contentApp, jobject jReadSuccessHandler, jobject jReadFailureHandler) +{ + chip::DeviceLayer::StackLock lock; + + ChipLogProgress(AppServer, "JNI_METHOD applicationBasic_1readVendorID called"); + + TargetEndpointInfo endpoint; + CHIP_ERROR err = convertJContentAppToTargetEndpointInfo(contentApp, endpoint); + VerifyOrExit(err == CHIP_NO_ERROR, + ChipLogError(AppServer, "Conversion from jobject contentApp to TargetEndpointInfo * failed: %" CHIP_ERROR_FORMAT, + err.Format())); + + err = TvCastingAppJNIMgr().getVendorIDSuccessHandler().SetUp(env, jReadSuccessHandler); + VerifyOrExit(CHIP_NO_ERROR == err, ChipLogError(AppServer, "SuccessHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_VendorID).SetUp(env, jReadFailureHandler); + VerifyOrExit(CHIP_NO_ERROR == err, + ChipLogError(AppServer, "ReadFailureHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = CastingServer::GetInstance()->ApplicationBasic_ReadVendorID( + &endpoint, nullptr, + [](void * context, chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType responseData) { + TvCastingAppJNIMgr().getVendorIDSuccessHandler().Handle(responseData); + }, + [](void * context, CHIP_ERROR err) { TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_VendorID).Handle(err); }); + + VerifyOrExit(CHIP_NO_ERROR == err, + ChipLogError(AppServer, "CastingServer.applicationBasic_ReadVendorID failed %" CHIP_ERROR_FORMAT, err.Format())); + +exit: + if (err != CHIP_NO_ERROR) + { + return false; + } + + return true; +} + +JNI_METHOD(jboolean, applicationBasic_1readApplicationName) +(JNIEnv * env, jobject, jobject contentApp, jobject jReadSuccessHandler, jobject jReadFailureHandler) +{ + chip::DeviceLayer::StackLock lock; + + ChipLogProgress(AppServer, "JNI_METHOD applicationBasic_1readApplicationName called"); + + TargetEndpointInfo endpoint; + CHIP_ERROR err = convertJContentAppToTargetEndpointInfo(contentApp, endpoint); + VerifyOrExit(err == CHIP_NO_ERROR, + ChipLogError(AppServer, "Conversion from jobject contentApp to TargetEndpointInfo * failed: %" CHIP_ERROR_FORMAT, + err.Format())); + + err = TvCastingAppJNIMgr().getApplicationNameSuccessHandler().SetUp(env, jReadSuccessHandler); + VerifyOrExit(CHIP_NO_ERROR == err, ChipLogError(AppServer, "SuccessHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_ApplicationName).SetUp(env, jReadFailureHandler); + VerifyOrExit(CHIP_NO_ERROR == err, + ChipLogError(AppServer, "ReadFailureHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = CastingServer::GetInstance()->ApplicationBasic_ReadApplicationName( + &endpoint, nullptr, + [](void * context, + chip::app::Clusters::ApplicationBasic::Attributes::ApplicationName::TypeInfo::DecodableArgType responseData) { + TvCastingAppJNIMgr().getApplicationNameSuccessHandler().Handle(responseData); + }, + [](void * context, CHIP_ERROR err) { + TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_ApplicationName).Handle(err); + }); + + VerifyOrExit( + CHIP_NO_ERROR == err, + ChipLogError(AppServer, "CastingServer.applicationBasic_ReadApplicationName failed %" CHIP_ERROR_FORMAT, err.Format())); + +exit: + if (err != CHIP_NO_ERROR) + { + return false; + } + + return true; +} + +JNI_METHOD(jboolean, applicationBasic_1readProductID) +(JNIEnv * env, jobject, jobject contentApp, jobject jReadSuccessHandler, jobject jReadFailureHandler) +{ + chip::DeviceLayer::StackLock lock; + + ChipLogProgress(AppServer, "JNI_METHOD applicationBasic_1readProductID called"); + + TargetEndpointInfo endpoint; + CHIP_ERROR err = convertJContentAppToTargetEndpointInfo(contentApp, endpoint); + VerifyOrExit(err == CHIP_NO_ERROR, + ChipLogError(AppServer, "Conversion from jobject contentApp to TargetEndpointInfo * failed: %" CHIP_ERROR_FORMAT, + err.Format())); + + err = TvCastingAppJNIMgr().getProductIDSuccessHandler().SetUp(env, jReadSuccessHandler); + VerifyOrExit(CHIP_NO_ERROR == err, ChipLogError(AppServer, "SuccessHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_ProductID).SetUp(env, jReadFailureHandler); + VerifyOrExit(CHIP_NO_ERROR == err, + ChipLogError(AppServer, "ReadFailureHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = CastingServer::GetInstance()->ApplicationBasic_ReadProductID( + &endpoint, nullptr, + [](void * context, chip::app::Clusters::ApplicationBasic::Attributes::ProductID::TypeInfo::DecodableArgType responseData) { + TvCastingAppJNIMgr().getProductIDSuccessHandler().Handle(responseData); + }, + [](void * context, CHIP_ERROR err) { TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_ProductID).Handle(err); }); + + VerifyOrExit(CHIP_NO_ERROR == err, + ChipLogError(AppServer, "CastingServer.applicationBasic_ReadProductID failed %" CHIP_ERROR_FORMAT, err.Format())); + +exit: + if (err != CHIP_NO_ERROR) + { + return false; + } + + return true; +} + +JNI_METHOD(jboolean, applicationBasic_1readApplicationVersion) +(JNIEnv * env, jobject, jobject contentApp, jobject jReadSuccessHandler, jobject jReadFailureHandler) +{ + chip::DeviceLayer::StackLock lock; + + ChipLogProgress(AppServer, "JNI_METHOD applicationBasic_1readApplicationVersion called"); + + TargetEndpointInfo endpoint; + CHIP_ERROR err = convertJContentAppToTargetEndpointInfo(contentApp, endpoint); + VerifyOrExit(err == CHIP_NO_ERROR, + ChipLogError(AppServer, "Conversion from jobject contentApp to TargetEndpointInfo * failed: %" CHIP_ERROR_FORMAT, + err.Format())); + + err = TvCastingAppJNIMgr().getApplicationVersionSuccessHandler().SetUp(env, jReadSuccessHandler); + VerifyOrExit(CHIP_NO_ERROR == err, ChipLogError(AppServer, "SuccessHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_ApplicationVersion).SetUp(env, jReadFailureHandler); + VerifyOrExit(CHIP_NO_ERROR == err, + ChipLogError(AppServer, "ReadFailureHandler.SetUp failed %" CHIP_ERROR_FORMAT, err.Format())); + + err = CastingServer::GetInstance()->ApplicationBasic_ReadApplicationVersion( + &endpoint, nullptr, + [](void * context, + chip::app::Clusters::ApplicationBasic::Attributes::ApplicationVersion::TypeInfo::DecodableArgType responseData) { + TvCastingAppJNIMgr().getApplicationVersionSuccessHandler().Handle(responseData); + }, + [](void * context, CHIP_ERROR err) { + TvCastingAppJNIMgr().getReadFailureHandler(ApplicationBasic_ApplicationVersion).Handle(err); + }); + + VerifyOrExit( + CHIP_NO_ERROR == err, + ChipLogError(AppServer, "CastingServer.applicationBasic_ReadApplicationVersion failed %" CHIP_ERROR_FORMAT, err.Format())); + +exit: + if (err != CHIP_NO_ERROR) + { + return false; + } + + return true; +} diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/TvCastingApp-JNI.h b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/TvCastingApp-JNI.h index 71bd0bddb466be..7d2b384b3718ae 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/TvCastingApp-JNI.h +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/TvCastingApp-JNI.h @@ -45,6 +45,8 @@ class TvCastingAppJNI return mMediaCommandResponseHandler[name]; } + FailureHandlerJNI & getReadFailureHandler(enum MediaAttributeName name) { return mReadFailureHandler[name]; } + FailureHandlerJNI & getSubscriptionReadFailureHandler(enum MediaAttributeName name) { return mSubscriptionReadFailureHandler[name]; @@ -80,6 +82,15 @@ class TvCastingAppJNI ProductIDSuccessHandlerJNI & getProductIDSuccessHandler() { return mProductIDSuccessHandlerJNI; } ApplicationVersionSuccessHandlerJNI & getApplicationVersionSuccessHandler() { return mApplicationVersionSuccessHandlerJNI; } + VendorNameSuccessHandlerJNI & getReadVendorNameSuccessHandler() { return mReadVendorNameSuccessHandlerJNI; } + VendorIDSuccessHandlerJNI & getReadVendorIDSuccessHandler() { return mReadVendorIDSuccessHandlerJNI; } + ApplicationNameSuccessHandlerJNI & getReadApplicationNameSuccessHandler() { return mReadApplicationNameSuccessHandlerJNI; } + ProductIDSuccessHandlerJNI & getReadProductIDSuccessHandler() { return mReadProductIDSuccessHandlerJNI; } + ApplicationVersionSuccessHandlerJNI & getReadApplicationVersionSuccessHandler() + { + return mReadApplicationVersionSuccessHandlerJNI; + } + private: friend TvCastingAppJNI & TvCastingAppJNIMgr(); @@ -97,6 +108,7 @@ class TvCastingAppJNI MatterCallbackHandlerJNI mMediaCommandResponseHandler[MEDIA_COMMAND_COUNT]; FailureHandlerJNI mSubscriptionReadFailureHandler[MEDIA_ATTRIBUTE_COUNT]; SubscriptionEstablishedHandlerJNI mSubscriptionEstablishedHandler[MEDIA_ATTRIBUTE_COUNT]; + FailureHandlerJNI mReadFailureHandler[MEDIA_ATTRIBUTE_COUNT]; CurrentStateSuccessHandlerJNI mCurrentStateSuccessHandlerJNI; DurationSuccessHandlerJNI mDurationSuccessHandlerJNI; @@ -119,6 +131,12 @@ class TvCastingAppJNI ApplicationNameSuccessHandlerJNI mApplicationNameSuccessHandlerJNI; ProductIDSuccessHandlerJNI mProductIDSuccessHandlerJNI; ApplicationVersionSuccessHandlerJNI mApplicationVersionSuccessHandlerJNI; + + VendorNameSuccessHandlerJNI mReadVendorNameSuccessHandlerJNI; + VendorIDSuccessHandlerJNI mReadVendorIDSuccessHandlerJNI; + ApplicationNameSuccessHandlerJNI mReadApplicationNameSuccessHandlerJNI; + ProductIDSuccessHandlerJNI mReadProductIDSuccessHandlerJNI; + ApplicationVersionSuccessHandlerJNI mReadApplicationVersionSuccessHandlerJNI; }; inline class TvCastingAppJNI & TvCastingAppJNIMgr() diff --git a/examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_commissioning.xml b/examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_connection.xml similarity index 94% rename from examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_commissioning.xml rename to examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_connection.xml index c75708136c4090..f0381f3e5ae773 100644 --- a/examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_commissioning.xml +++ b/examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_connection.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".CommissioningFragment" + tools:context=".ConnectionFragment" android:padding="10sp"> + +#ifndef AppParameters_h +#define AppParameters_h + +@interface AppParameters : NSObject + +@property NSData * rotatingDeviceIdUniqueId; + +- (AppParameters *)initWithRotatingDeviceIdUniqueId:(NSData *)rotatingDeviceIdUniqueId; + +@end + +#endif /* AppParameters_h */ diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/AppParameters.m b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/AppParameters.m new file mode 100644 index 00000000000000..287d4ab40fd57c --- /dev/null +++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/AppParameters.m @@ -0,0 +1,33 @@ +/** + * + * Copyright (c) 2020-2022 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. + */ + +#import + +#import "AppParameters.h" + +@implementation AppParameters + +- (AppParameters *)initWithRotatingDeviceIdUniqueId:(NSData *)rotatingDeviceIdUniqueId +{ + self = [super init]; + if (self) { + _rotatingDeviceIdUniqueId = rotatingDeviceIdUniqueId; + } + return self; +} + +@end diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.h index f828ae5f01c70f..b91f930f5327a7 100644 --- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.h +++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.h @@ -15,6 +15,7 @@ * limitations under the License. */ +#import "AppParameters.h" #import "ContentApp.h" #import "ContentLauncherTypes.h" #import "DiscoveredNodeData.h" @@ -32,6 +33,10 @@ + (CastingServerBridge * _Nullable)getSharedInstance; +- (void)initApp:(AppParameters * _Nullable)appParameters + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + initAppStatusHandler:(nullable void (^)(bool))initAppStatusHandler; + /*! @brief Browse for on-network commissioner TVs @@ -1003,6 +1008,100 @@ successCallback:(void (^_Nonnull)(NSString * _Nonnull))successCallback failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback subscriptionEstablishedCallback:(void (^_Nonnull)())subscriptionEstablishedCallback; -@end +/*! + @brief Read ApplicationBasic:VendorName + + @param contentApp Content app endpoint to target + + @param clientQueue Queue to invoke callbacks on + + @param requestSentHandler Handler to call on sending the request + + @param successCallback Callback for when a read report is successfully received + + @param failureCallback Callback for when there is a failure in receiving a read report + */ +- (void)applicationBasic_readVendorName:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(NSString * _Nonnull))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback; + +/*! + @brief Read ApplicationBasic:VendorID + + @param contentApp Content app endpoint to target + + @param clientQueue Queue to invoke callbacks on + + @param requestSentHandler Handler to call on sending the request + + @param successCallback Callback for when a read report is successfully received + + @param failureCallback Callback for when there is a failure in receiving a read report + */ +- (void)applicationBasic_readVendorID:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(NSNumber * _Nonnull))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback; + +/*! + @brief Read ApplicationBasic:ApplicationName + + @param contentApp Content app endpoint to target + + @param clientQueue Queue to invoke callbacks on + + @param requestSentHandler Handler to call on sending the request + + @param successCallback Callback for when a read report is successfully received + + @param failureCallback Callback for when there is a failure in receiving a read report + */ +- (void)applicationBasic_readApplicationName:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(NSString * _Nonnull))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback; + +/*! + @brief Read ApplicationBasic:ProductID + + @param contentApp Content app endpoint to target + + @param clientQueue Queue to invoke callbacks on + + @param requestSentHandler Handler to call on sending the request + + @param successCallback Callback for when a read report is successfully received + + @param failureCallback Callback for when there is a failure in receiving a read report + */ +- (void)applicationBasic_readProductID:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(uint16_t))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback; + +/*! + @brief Read ApplicationBasic:ApplicationVersion + + @param contentApp Content app endpoint to target + + @param clientQueue Queue to invoke callbacks on + + @param requestSentHandler Handler to call on sending the request + + @param successCallback Callback for when a read report is successfully received + + @param failureCallback Callback for when there is a failure in receiving a read report + */ +- (void)applicationBasic_readApplicationVersion:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(NSString * _Nonnull))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback; +@end #endif /* CastingServerBridge_h */ diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.mm index dd89e32cbd42fe..c4ffd87f2a09d8 100644 --- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.mm +++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/CastingServerBridge.mm @@ -54,6 +54,10 @@ @interface CastingServerBridge () @property NSMutableDictionary * subscriptionReadFailureCallbacks; +@property NSMutableDictionary * readSuccessCallbacks; + +@property NSMutableDictionary * readFailureCallbacks; + @end @implementation CastingServerBridge @@ -122,12 +126,35 @@ - (instancetype)init _subscriptionReadFailureCallbacks = [NSMutableDictionary dictionary]; chip::DeviceLayer::PlatformMgrImpl().StartEventLoopTask(); - - CastingServer::GetInstance()->Init(); } return self; } +- (void)initApp:(AppParameters * _Nullable)appParameters + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + initAppStatusHandler:(nullable void (^)(bool))initAppStatusHandler +{ + ChipLogProgress(AppServer, "CastingServerBridge().initApp() called"); + + dispatch_async(_chipWorkQueue, ^{ + bool initAppStatus = true; + + AppParams appParams; + + CHIP_ERROR err = CastingServer::GetInstance()->Init(); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "CastingServerBridge().initApp() failed: %" CHIP_ERROR_FORMAT, err.Format()); + initAppStatus = false; + } + + dispatch_async(clientQueue, ^{ + initAppStatusHandler(initAppStatus); + }); + }); + + CastingServer::GetInstance()->Init(); +} + - (void)discoverCommissioners:(dispatch_queue_t _Nonnull)clientQueue discoveryRequestSentHandler:(nullable void (^)(bool))discoveryRequestSentHandler { @@ -153,11 +180,17 @@ - (void)getDiscoveredCommissioner:(int)index ChipLogProgress(AppServer, "CastingServerBridge().getDiscoveredCommissioner() called"); dispatch_async(_chipWorkQueue, ^{ + chip::Optional associatedConnectableVideoPlayer; DiscoveredNodeData * commissioner = nil; const chip::Dnssd::DiscoveredNodeData * cppDiscoveredNodeData - = CastingServer::GetInstance()->GetDiscoveredCommissioner(index); + = CastingServer::GetInstance()->GetDiscoveredCommissioner(index, associatedConnectableVideoPlayer); if (cppDiscoveredNodeData != nullptr) { commissioner = [ConversionUtils convertToObjCDiscoveredNodeDataFrom:cppDiscoveredNodeData]; + if (associatedConnectableVideoPlayer.HasValue()) { + VideoPlayer * connectableVideoPlayer = + [ConversionUtils convertToObjCVideoPlayerFrom:associatedConnectableVideoPlayer.Value()]; + [commissioner setConnectableVideoPlayer:connectableVideoPlayer]; + } } dispatch_async(clientQueue, ^{ @@ -1725,4 +1758,185 @@ - (void)applicationBasic_subscribeApplicationVersion:(ContentApp * _Nonnull)cont }); } +- (void)applicationBasic_readVendorName:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(NSString * _Nonnull))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback +{ + ChipLogProgress(AppServer, "CastingServerBridge().applicationBasic_readVendorName() called on Content App with endpoint ID %d", + contentApp.endpointId); + + [_readSuccessCallbacks setObject:successCallback forKey:@"applicationBasic_readVendorName"]; + [_readFailureCallbacks setObject:failureCallback forKey:@"applicationBasic_readVendorName"]; + + dispatch_async(_chipWorkQueue, ^{ + TargetEndpointInfo endpoint; + [ConversionUtils convertToCppTargetEndpointInfoFrom:contentApp outTargetEndpointInfo:endpoint]; + + CHIP_ERROR err = CastingServer::GetInstance()->ApplicationBasic_ReadVendorName( + &endpoint, nullptr, + [](void * context, + chip::app::Clusters::ApplicationBasic::Attributes::VendorName::TypeInfo::DecodableArgType vendorName) { + void (^callback)(NSString * _Nonnull) = [[CastingServerBridge getSharedInstance].subscriptionReadSuccessCallbacks + objectForKey:@"applicationBasic_readVendorName"]; + callback([NSString stringWithUTF8String:vendorName.data()]); + }, + [](void * context, CHIP_ERROR err) { + void (^callback)(MatterError *) = [[CastingServerBridge getSharedInstance].subscriptionReadFailureCallbacks + objectForKey:@"applicationBasic_readVendorName"]; + callback([[MatterError alloc] initWithCode:err.AsInteger() message:[NSString stringWithUTF8String:err.AsString()]]); + }); + dispatch_async(clientQueue, ^{ + requestSentHandler([[MatterError alloc] initWithCode:err.AsInteger() + message:[NSString stringWithUTF8String:err.AsString()]]); + }); + }); +} + +- (void)applicationBasic_readVendorID:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(NSNumber * _Nonnull))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback +{ + ChipLogProgress(AppServer, "CastingServerBridge().applicationBasic_readVendorID() called on Content App with endpoint ID %d", + contentApp.endpointId); + + [_readSuccessCallbacks setObject:successCallback forKey:@"applicationBasic_readVendorID"]; + [_readFailureCallbacks setObject:failureCallback forKey:@"applicationBasic_readVendorID"]; + + dispatch_async(_chipWorkQueue, ^{ + TargetEndpointInfo endpoint; + [ConversionUtils convertToCppTargetEndpointInfoFrom:contentApp outTargetEndpointInfo:endpoint]; + + CHIP_ERROR err = CastingServer::GetInstance()->ApplicationBasic_ReadVendorID( + &endpoint, nullptr, + [](void * context, chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType vendorID) { + void (^callback)(NSNumber * _Nonnull) = [[CastingServerBridge getSharedInstance].subscriptionReadSuccessCallbacks + objectForKey:@"applicationBasic_readVendorID"]; + callback(@(vendorID)); + }, + [](void * context, CHIP_ERROR err) { + void (^callback)(MatterError *) = [[CastingServerBridge getSharedInstance].subscriptionReadFailureCallbacks + objectForKey:@"applicationBasic_readVendorID"]; + callback([[MatterError alloc] initWithCode:err.AsInteger() message:[NSString stringWithUTF8String:err.AsString()]]); + }); + dispatch_async(clientQueue, ^{ + requestSentHandler([[MatterError alloc] initWithCode:err.AsInteger() + message:[NSString stringWithUTF8String:err.AsString()]]); + }); + }); +} + +- (void)applicationBasic_readApplicationName:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(NSString * _Nonnull))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback +{ + ChipLogProgress(AppServer, + "CastingServerBridge().applicationBasic_readApplicationName() called on Content App with endpoint ID %d", + contentApp.endpointId); + + [_readSuccessCallbacks setObject:successCallback forKey:@"applicationBasic_readApplicationName"]; + [_readFailureCallbacks setObject:failureCallback forKey:@"applicationBasic_readApplicationName"]; + + dispatch_async(_chipWorkQueue, ^{ + TargetEndpointInfo endpoint; + [ConversionUtils convertToCppTargetEndpointInfoFrom:contentApp outTargetEndpointInfo:endpoint]; + + CHIP_ERROR err = CastingServer::GetInstance()->ApplicationBasic_ReadApplicationName( + &endpoint, nullptr, + [](void * context, + chip::app::Clusters::ApplicationBasic::Attributes::ApplicationName::TypeInfo::DecodableArgType applicationName) { + void (^callback)(NSString * _Nonnull) = [[CastingServerBridge getSharedInstance].subscriptionReadSuccessCallbacks + objectForKey:@"applicationBasic_readApplicationName"]; + callback([NSString stringWithUTF8String:applicationName.data()]); + }, + [](void * context, CHIP_ERROR err) { + void (^callback)(MatterError *) = [[CastingServerBridge getSharedInstance].subscriptionReadFailureCallbacks + objectForKey:@"applicationBasic_readApplicationName"]; + callback([[MatterError alloc] initWithCode:err.AsInteger() message:[NSString stringWithUTF8String:err.AsString()]]); + }); + dispatch_async(clientQueue, ^{ + requestSentHandler([[MatterError alloc] initWithCode:err.AsInteger() + message:[NSString stringWithUTF8String:err.AsString()]]); + }); + }); +} + +- (void)applicationBasic_readProductID:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(uint16_t))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback +{ + ChipLogProgress(AppServer, "CastingServerBridge().applicationBasic_readProductID() called on Content App with endpoint ID %d", + contentApp.endpointId); + + [_readSuccessCallbacks setObject:successCallback forKey:@"applicationBasic_readProductID"]; + [_readFailureCallbacks setObject:failureCallback forKey:@"applicationBasic_readProductID"]; + + dispatch_async(_chipWorkQueue, ^{ + TargetEndpointInfo endpoint; + [ConversionUtils convertToCppTargetEndpointInfoFrom:contentApp outTargetEndpointInfo:endpoint]; + + CHIP_ERROR err = CastingServer::GetInstance()->ApplicationBasic_ReadProductID( + &endpoint, nullptr, + [](void * context, chip::app::Clusters::ApplicationBasic::Attributes::ProductID::TypeInfo::DecodableArgType productID) { + void (^callback)(uint16_t) = [[CastingServerBridge getSharedInstance].subscriptionReadSuccessCallbacks + objectForKey:@"applicationBasic_readProductID"]; + callback(productID); + }, + [](void * context, CHIP_ERROR err) { + void (^callback)(MatterError *) = [[CastingServerBridge getSharedInstance].subscriptionReadFailureCallbacks + objectForKey:@"applicationBasic_readProductID"]; + callback([[MatterError alloc] initWithCode:err.AsInteger() message:[NSString stringWithUTF8String:err.AsString()]]); + }); + dispatch_async(clientQueue, ^{ + requestSentHandler([[MatterError alloc] initWithCode:err.AsInteger() + message:[NSString stringWithUTF8String:err.AsString()]]); + }); + }); +} + +- (void)applicationBasic_readApplicationVersion:(ContentApp * _Nonnull)contentApp + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + requestSentHandler:(void (^_Nonnull)(MatterError * _Nonnull))requestSentHandler + successCallback:(void (^_Nonnull)(NSString * _Nonnull))successCallback + failureCallback:(void (^_Nonnull)(MatterError * _Nonnull))failureCallback +{ + ChipLogProgress(AppServer, + "CastingServerBridge().applicationBasic_readApplicationVersion() called on Content App with endpoint ID %d", + contentApp.endpointId); + + [_readSuccessCallbacks setObject:successCallback forKey:@"applicationBasic_readApplicationVersion"]; + [_readFailureCallbacks setObject:failureCallback forKey:@"applicationBasic_readApplicationVersion"]; + + dispatch_async(_chipWorkQueue, ^{ + TargetEndpointInfo endpoint; + [ConversionUtils convertToCppTargetEndpointInfoFrom:contentApp outTargetEndpointInfo:endpoint]; + + CHIP_ERROR err = CastingServer::GetInstance()->ApplicationBasic_ReadApplicationVersion( + &endpoint, nullptr, + [](void * context, + chip::app::Clusters::ApplicationBasic::Attributes::ApplicationVersion::TypeInfo::DecodableArgType + applicationVersion) { + void (^callback)(NSString * _Nonnull) = [[CastingServerBridge getSharedInstance].subscriptionReadSuccessCallbacks + objectForKey:@"applicationBasic_readApplicationVersion"]; + callback([NSString stringWithUTF8String:applicationVersion.data()]); + }, + [](void * context, CHIP_ERROR err) { + void (^callback)(MatterError *) = [[CastingServerBridge getSharedInstance].subscriptionReadFailureCallbacks + objectForKey:@"applicationBasic_readApplicationVersion"]; + callback([[MatterError alloc] initWithCode:err.AsInteger() message:[NSString stringWithUTF8String:err.AsString()]]); + }); + dispatch_async(clientQueue, ^{ + requestSentHandler([[MatterError alloc] initWithCode:err.AsInteger() + message:[NSString stringWithUTF8String:err.AsString()]]); + }); + }); +} + @end diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/ConversionUtils.hpp b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/ConversionUtils.hpp index df6a0360816ce8..bf92cf09a0069b 100644 --- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/ConversionUtils.hpp +++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/ConversionUtils.hpp @@ -17,10 +17,12 @@ #import +#import "AppParameters.h" #import "ContentApp.h" #import "DiscoveredNodeData.h" #import "VideoPlayer.h" +#import #import #import #include @@ -32,6 +34,8 @@ /** * @brief Objective C to C++ converters */ ++ (CHIP_ERROR)convertToCppAppParamsInfoFrom:(AppParameters * _Nonnull)objCAppParameters outAppParams:(AppParams &)outAppParams; + + (CHIP_ERROR)convertToCppTargetEndpointInfoFrom:(ContentApp * _Nonnull)objCContentApp outTargetEndpointInfo:(TargetEndpointInfo &)outTargetEndpointInfo; diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/ConversionUtils.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/ConversionUtils.mm index ca626201fdf236..f71cab0bf0e1c6 100644 --- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/ConversionUtils.mm +++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/ConversionUtils.mm @@ -20,6 +20,19 @@ @implementation ConversionUtils ++ (CHIP_ERROR)convertToCppAppParamsInfoFrom:(AppParameters * _Nonnull)objCAppParameters outAppParams:(AppParams &)outAppParams +{ + VerifyOrReturnError(objCAppParameters != nil, CHIP_ERROR_INVALID_ARGUMENT); + + if (objCAppParameters.rotatingDeviceIdUniqueId != nil) { + chip::ByteSpan rotatingDeviceIdUniqueId + = chip::ByteSpan(static_cast(objCAppParameters.rotatingDeviceIdUniqueId.bytes), + objCAppParameters.rotatingDeviceIdUniqueId.length); + outAppParams.SetRotatingDeviceIdUniqueId(MakeOptional(rotatingDeviceIdUniqueId)); + } + return CHIP_NO_ERROR; +} + + (CHIP_ERROR)convertToCppTargetEndpointInfoFrom:(ContentApp * _Nonnull)objCContentApp outTargetEndpointInfo:(TargetEndpointInfo &)outTargetEndpointInfo { diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/DiscoveredNodeData.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/DiscoveredNodeData.h index 24240cc4a4acda..f4cf95f4be7e6a 100644 --- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/DiscoveredNodeData.h +++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/DiscoveredNodeData.h @@ -17,6 +17,8 @@ #import +#import "VideoPlayer.h" + #ifndef DiscoveredNodeData_h #define DiscoveredNodeData_h @@ -54,6 +56,12 @@ - (DiscoveredNodeData *)initWithDeviceName:(NSString *)deviceName vendorId:(uint16_t)vendorId productId:(uint16_t)productId; +- (bool)isPreCommissioned; + +- (VideoPlayer *)getConnectableVideoPlayer; + +- (void)setConnectableVideoPlayer:(VideoPlayer *)videoPlayer; + @end #endif /* DiscoveredNodeData_h */ diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/DiscoveredNodeData.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/DiscoveredNodeData.mm index 541352b3d31bf9..f8c85e52269dbb 100644 --- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/DiscoveredNodeData.mm +++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/DiscoveredNodeData.mm @@ -20,6 +20,12 @@ #import "DiscoveredNodeData.h" #include +@interface DiscoveredNodeData () + +@property (nonatomic) VideoPlayer * connectableVideoPlayer; + +@end + @implementation DiscoveredNodeData - (DiscoveredNodeData *)initWithDeviceName:(NSString *)deviceName vendorId:(uint16_t)vendorId productId:(uint16_t)productId @@ -35,7 +41,12 @@ - (DiscoveredNodeData *)initWithDeviceName:(NSString *)deviceName vendorId:(uint - (NSString *)description { - return [NSString stringWithFormat:@"%@ with Product ID: %d and Vendor ID: %d", _deviceName, _productId, _vendorId]; + if ([self isPreCommissioned]) { + return [NSString + stringWithFormat:@"%@ with Product ID: %d and Vendor ID: %d [Pre-Commissioned]", _deviceName, _productId, _vendorId]; + } else { + return [NSString stringWithFormat:@"%@ with Product ID: %d and Vendor ID: %d", _deviceName, _productId, _vendorId]; + } } - (BOOL)isEqualToDiscoveredNodeData:(DiscoveredNodeData *)other @@ -70,4 +81,19 @@ - (NSUInteger)hash return result; } +- (void)setConnectableVideoPlayer:(VideoPlayer * _Nonnull)videoPlayer +{ + _connectableVideoPlayer = videoPlayer; +} + +- (bool)isPreCommissioned +{ + return _connectableVideoPlayer != nil; +} + +- (VideoPlayer *)getConnectableVideoPlayer +{ + return _connectableVideoPlayer; +} + @end diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissionerDiscoveryView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissionerDiscoveryView.swift index 3ee2ac30d4b724..7a33fc7b19e271 100644 --- a/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissionerDiscoveryView.swift +++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissionerDiscoveryView.swift @@ -55,7 +55,16 @@ struct CommissionerDiscoveryView: View { Text("Select a commissioner video player...") ForEach(viewModel.commissioners) { commissioner in NavigationLink( - destination: CommissioningView(_selectedCommissioner: commissioner), + destination: { + if(commissioner.isPreCommissioned()) + { + ConnectionView(_selectedVideoPlayer: commissioner.getConnectableVideoPlayer()) + } + else + { + CommissioningView(_selectedCommissioner: commissioner) + } + }, label: { Text(commissioner.description) } diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentView.swift index e7ca12c9b720cd..b281da172435aa 100644 --- a/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentView.swift +++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentView.swift @@ -20,7 +20,6 @@ import SwiftUI struct ContentView: View { var body: some View { NavigationView { - //CommissionerDiscoveryView() StartFromCacheView() } } diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/TvCastingApp.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/TvCastingApp.swift index 27dce5b63f55f1..26e4768485d13e 100644 --- a/examples/tv-casting-app/darwin/TvCasting/TvCasting/TvCastingApp.swift +++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/TvCastingApp.swift @@ -16,12 +16,33 @@ */ import SwiftUI +import os.log @main struct TvCastingApp: App { + let Log = Logger(subsystem: "com.matter.casting", + category: "TvCastingApp") + var body: some Scene { WindowGroup { ContentView() + .onAppear(perform: { + if let castingServerBridge = CastingServerBridge.getSharedInstance() + { + let appParameters: AppParameters = AppParameters() + + var rotatingDeviceIdUniqueId: [UInt8] = [UInt8](repeating: 0, count: 16 ) + for i in (0...15) + { + rotatingDeviceIdUniqueId[i] = UInt8.random(in: 0..<255) + } + appParameters.rotatingDeviceIdUniqueId = Data(rotatingDeviceIdUniqueId) + + castingServerBridge.initApp(appParameters, clientQueue: DispatchQueue.main, initAppStatusHandler: { (result: Bool) -> () in + self.Log.info("initApp result \(result)") + }) + } + }) } } } diff --git a/examples/tv-casting-app/linux/CastingUtils.cpp b/examples/tv-casting-app/linux/CastingUtils.cpp index 760b0435124c47..294339bed585a1 100644 --- a/examples/tv-casting-app/linux/CastingUtils.cpp +++ b/examples/tv-casting-app/linux/CastingUtils.cpp @@ -41,7 +41,9 @@ CHIP_ERROR DiscoverCommissioners() CHIP_ERROR RequestCommissioning(int index) { - const Dnssd::DiscoveredNodeData * selectedCommissioner = CastingServer::GetInstance()->GetDiscoveredCommissioner(index); + chip::Optional associatedConnectableVideoPlayer; + const Dnssd::DiscoveredNodeData * selectedCommissioner = + CastingServer::GetInstance()->GetDiscoveredCommissioner(index, associatedConnectableVideoPlayer); if (selectedCommissioner == nullptr) { ChipLogError(AppServer, "No such commissioner with index %d exists", index); @@ -89,11 +91,19 @@ void InitCommissioningFlow(intptr_t commandArg) // Display discovered commissioner TVs to ask user to select one for (int i = 0; i < CHIP_DEVICE_CONFIG_MAX_DISCOVERED_NODES; i++) { - const Dnssd::DiscoveredNodeData * commissioner = CastingServer::GetInstance()->GetDiscoveredCommissioner(i); + chip::Optional associatedConnectableVideoPlayer; + const Dnssd::DiscoveredNodeData * commissioner = + CastingServer::GetInstance()->GetDiscoveredCommissioner(i, associatedConnectableVideoPlayer); if (commissioner != nullptr) { ChipLogProgress(AppServer, "Discovered Commissioner #%d", commissionerCount++); commissioner->LogDetail(); + if (associatedConnectableVideoPlayer.HasValue()) + { + TargetVideoPlayerInfo * targetVideoPlayerInfo = associatedConnectableVideoPlayer.Value(); + ChipLogProgress(AppServer, "Previously connected with nodeId 0x" ChipLogFormatX64 " fabricIndex: %d", + ChipLogValueX64(targetVideoPlayerInfo->GetNodeId()), targetVideoPlayerInfo->GetFabricIndex()); + } } } diff --git a/examples/tv-casting-app/tv-casting-common/BUILD.gn b/examples/tv-casting-app/tv-casting-common/BUILD.gn index 1c356d5cb36b9e..179fa86602367a 100644 --- a/examples/tv-casting-app/tv-casting-common/BUILD.gn +++ b/examples/tv-casting-app/tv-casting-common/BUILD.gn @@ -47,6 +47,7 @@ chip_data_model("tv-casting-common") { "${chip_root}/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp", "commands/clusters/ModelCommand.cpp", "commands/common/CHIPCommand.cpp", + "include/AppParams.h", "include/ApplicationBasic.h", "include/ApplicationLauncher.h", "include/CastingServer.h", @@ -57,11 +58,13 @@ chip_data_model("tv-casting-common") { "include/MediaBase.h", "include/MediaCommandBase.h", "include/MediaPlayback.h", + "include/MediaReadBase.h", "include/MediaSubscriptionBase.h", "include/PersistenceManager.h", "include/TargetEndpointInfo.h", "include/TargetNavigator.h", "include/TargetVideoPlayerInfo.h", + "src/AppParams.cpp", "src/ApplicationLauncher.cpp", "src/CastingServer.cpp", "src/Channel.cpp", diff --git a/examples/tv-casting-app/tv-casting-common/include/AppParams.h b/examples/tv-casting-app/tv-casting-common/include/AppParams.h new file mode 100644 index 00000000000000..7a42c09b501dda --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/include/AppParams.h @@ -0,0 +1,42 @@ +/* + * + * Copyright (c) 2022 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 +#include + +/** + * @brief Parameters passed to the CastingServer at the time of startup (i.e. init call) + */ +class AppParams +{ +public: + AppParams() {} +#if CHIP_ENABLE_ROTATING_DEVICE_ID + AppParams(chip::Optional rotatingDeviceIdUniqueId); + void SetRotatingDeviceIdUniqueId(chip::Optional rotatingDeviceIdUniqueId); + chip::Optional GetRotatingDeviceIdUniqueId(); +#endif // CHIP_ENABLE_ROTATING_DEVICE_ID + +private: +#if CHIP_ENABLE_ROTATING_DEVICE_ID + // if this Optional.hasValue = false, a random one is generated by CastingServer.Init() + chip::Optional mRotatingDeviceIdUniqueId; +#endif // CHIP_ENABLE_ROTATING_DEVICE_ID +}; diff --git a/examples/tv-casting-app/tv-casting-common/include/ApplicationBasic.h b/examples/tv-casting-app/tv-casting-common/include/ApplicationBasic.h index d30d659782b18a..ec009de429a8e6 100644 --- a/examples/tv-casting-app/tv-casting-common/include/ApplicationBasic.h +++ b/examples/tv-casting-app/tv-casting-common/include/ApplicationBasic.h @@ -16,6 +16,7 @@ * limitations under the License. */ +#include "MediaReadBase.h" #include "MediaSubscriptionBase.h" #include @@ -71,3 +72,54 @@ class AllowedVendorListSubscriber public: AllowedVendorListSubscriber() : MediaSubscriptionBase(chip::app::Clusters::ApplicationBasic::Id) {} }; + +// READER CLASSES + +class VendorNameReader : public MediaReadBase +{ +public: + VendorNameReader() : MediaReadBase(chip::app::Clusters::ApplicationBasic::Id) {} +}; + +class VendorIDReader : public MediaReadBase +{ +public: + VendorIDReader() : MediaReadBase(chip::app::Clusters::ApplicationBasic::Id) {} +}; + +class ApplicationNameReader : public MediaReadBase +{ +public: + ApplicationNameReader() : MediaReadBase(chip::app::Clusters::ApplicationBasic::Id) {} +}; + +class ProductIDReader : public MediaReadBase +{ +public: + ProductIDReader() : MediaReadBase(chip::app::Clusters::ApplicationBasic::Id) {} +}; + +class ApplicationReader : public MediaReadBase +{ +public: + ApplicationReader() : MediaReadBase(chip::app::Clusters::ApplicationBasic::Id) {} +}; + +class StatusReader : public MediaReadBase +{ +public: + StatusReader() : MediaReadBase(chip::app::Clusters::ApplicationBasic::Id) {} +}; + +class ApplicationVersionReader + : public MediaReadBase +{ +public: + ApplicationVersionReader() : MediaReadBase(chip::app::Clusters::ApplicationBasic::Id) {} +}; + +class AllowedVendorListReader : public MediaReadBase +{ +public: + AllowedVendorListReader() : MediaReadBase(chip::app::Clusters::ApplicationBasic::Id) {} +}; diff --git a/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h b/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h index bc50cb335e274f..ca3b9bd3ca5be8 100644 --- a/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h +++ b/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h @@ -53,3 +53,5 @@ // Enable some test-only interaction model APIs. #define CONFIG_BUILD_FOR_HOST_UNIT_TEST 1 + +#define CHIP_ENABLE_ROTATING_DEVICE_ID 1 diff --git a/examples/tv-casting-app/tv-casting-common/include/CastingServer.h b/examples/tv-casting-app/tv-casting-common/include/CastingServer.h index b77e2bd320604b..375ede1f6b8af3 100644 --- a/examples/tv-casting-app/tv-casting-common/include/CastingServer.h +++ b/examples/tv-casting-app/tv-casting-common/include/CastingServer.h @@ -18,6 +18,7 @@ #pragma once +#include "AppParams.h" #include "ApplicationBasic.h" #include "ApplicationLauncher.h" #include "Channel.h" @@ -51,10 +52,11 @@ class CastingServer void operator=(const CastingServer &) = delete; static CastingServer * GetInstance(); - void Init(); + CHIP_ERROR Init(AppParams * AppParams = nullptr); CHIP_ERROR DiscoverCommissioners(); - const chip::Dnssd::DiscoveredNodeData * GetDiscoveredCommissioner(int index); + const chip::Dnssd::DiscoveredNodeData * + GetDiscoveredCommissioner(int index, chip::Optional & outAssociatedConnectableVideoPlayer); CHIP_ERROR OpenBasicCommissioningWindow(std::function commissioningCompleteCallback, std::function onConnectionSuccess, std::function onConnectionFailure, @@ -333,6 +335,55 @@ class CastingServer chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval, chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished); + CHIP_ERROR + ApplicationBasic_ReadVendorName(TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::VendorName::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn); + CHIP_ERROR + ApplicationBasic_ReadVendorID(TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn); + CHIP_ERROR ApplicationBasic_ReadApplicationName( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::ApplicationName::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn); + CHIP_ERROR + ApplicationBasic_ReadProductID(TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::ProductID::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn); + CHIP_ERROR + ApplicationBasic_ReadApplication(TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::Application::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn); + CHIP_ERROR + ApplicationBasic_ReadStatus(TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::Status::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn); + CHIP_ERROR ApplicationBasic_ReadApplicationVersion( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::ApplicationVersion::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn); + CHIP_ERROR ApplicationBasic_ReadAllowedVendorList( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::AllowedVendorList::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn); + /* * @brief Channel cluster */ @@ -361,6 +412,8 @@ class CastingServer uint16_t mTargetVideoPlayerProductId = 0; uint16_t mTargetVideoPlayerDeviceType = 0; char mTargetVideoPlayerDeviceName[chip::Dnssd::kMaxDeviceNameLen + 1] = {}; + size_t mTargetVideoPlayerNumIPs = 0; // number of valid IP addresses + chip::Inet::IPAddress mTargetVideoPlayerIpAddress[chip::Dnssd::CommonResolutionData::kMaxIPAddresses]; chip::Controller::CommissionableNodeController mCommissionableNodeController; std::function mCommissioningCompleteCallback; @@ -441,6 +494,15 @@ class CastingServer ApplicationVersionSubscriber mApplicationVersionSubscriber; AllowedVendorListSubscriber mAllowedVendorListSubscriber; + VendorNameReader mVendorNameReader; + VendorIDReader mVendorIDReader; + ApplicationNameReader mApplicationNameReader; + ProductIDReader mProductIDReader; + ApplicationReader mApplicationReader; + StatusReader mStatusReader; + ApplicationVersionReader mApplicationVersionReader; + AllowedVendorListReader mAllowedVendorListReader; + /* * @brief Channel cluster */ diff --git a/examples/tv-casting-app/tv-casting-common/include/MediaReadBase.h b/examples/tv-casting-app/tv-casting-common/include/MediaReadBase.h new file mode 100644 index 00000000000000..3003a08a348f70 --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/include/MediaReadBase.h @@ -0,0 +1,42 @@ +/* + * + * Copyright (c) 2022 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 "MediaBase.h" + +template +class MediaReadBase : public MediaBase +{ +public: + MediaReadBase(chip::ClusterId clusterId) : MediaBase(clusterId) {} + + CHIP_ERROR ReadAttribute(void * context, + chip::Controller::ReadResponseSuccessCallback successFn, + chip::Controller::ReadResponseFailureCallback failureFn) + { + VerifyOrDieWithMsg(mTargetVideoPlayerInfo != nullptr, AppServer, "Target unknown"); + + auto deviceProxy = mTargetVideoPlayerInfo->GetOperationalDeviceProxy(); + ReturnErrorCodeIf(deviceProxy == nullptr || !deviceProxy->ConnectionReady(), CHIP_ERROR_PEER_NODE_NOT_FOUND); + + MediaClusterBase cluster(*deviceProxy->GetExchangeManager(), deviceProxy->GetSecureSession().Value(), mClusterId, + mTvEndpoint); + + return cluster.template ReadAttribute(context, successFn, failureFn); + } +}; diff --git a/examples/tv-casting-app/tv-casting-common/include/PersistenceManager.h b/examples/tv-casting-app/tv-casting-common/include/PersistenceManager.h index d9505e00391126..b1e7bd6c58130d 100644 --- a/examples/tv-casting-app/tv-casting-common/include/PersistenceManager.h +++ b/examples/tv-casting-app/tv-casting-common/include/PersistenceManager.h @@ -44,16 +44,20 @@ class PersistenceManager kEndpointIdTag, kClusterIdsContainerTag, kClusterIdTag, - kCastingDataVersionTag, + kCurrentCastingDataVersionTag, kVideoPlayerVendorIdTag, kVideoPlayerProductIdTag, kVideoPlayerDeviceTypeIdTag, kVideoPlayerDeviceNameTag, + kVideoPlayerNumIPsTag, + kVideoPlayerIPAddressTag, + kIpAddressesContainerTag, kContextTagMaxNum = UINT8_MAX }; - constexpr static size_t kCastingDataMaxBytes = 1024 * 100; // 100 KBs - constexpr static char * kCastingDataKey = (char *) "com.matter.casting"; - constexpr static uint32_t kCastingDataVersion = 1; + constexpr static size_t kCastingDataMaxBytes = 1024 * 100; // 100 KBs + constexpr static char * kCastingDataKey = (char *) "com.matter.casting"; + constexpr static uint32_t kCurrentCastingDataVersion = 1; + constexpr static uint32_t kSupportedCastingDataVersions[1] = { 1 }; }; diff --git a/examples/tv-casting-app/tv-casting-common/include/TargetVideoPlayerInfo.h b/examples/tv-casting-app/tv-casting-common/include/TargetVideoPlayerInfo.h index da58e8db11b63e..0f423e2aeb459a 100644 --- a/examples/tv-casting-app/tv-casting-common/include/TargetVideoPlayerInfo.h +++ b/examples/tv-casting-app/tv-casting-common/include/TargetVideoPlayerInfo.h @@ -43,6 +43,9 @@ class TargetVideoPlayerInfo chip::NodeId GetNodeId() const { return mNodeId; } chip::FabricIndex GetFabricIndex() const { return mFabricIndex; } const char * GetDeviceName() const { return mDeviceName; } + size_t GetNumIPs() const { return mNumIPs; } + const chip::Inet::IPAddress * GetIpAddresses() const { return mIpAddress; } + bool IsSameAs(const chip::Dnssd::DiscoveredNodeData * discoveredNodeData); chip::OperationalDeviceProxy * GetOperationalDeviceProxy() { @@ -56,7 +59,8 @@ class TargetVideoPlayerInfo CHIP_ERROR Initialize(chip::NodeId nodeId, chip::FabricIndex fabricIndex, std::function onConnectionSuccess, std::function onConnectionFailure, uint16_t vendorId = 0, uint16_t productId = 0, - uint16_t deviceType = 0, const char * deviceName = {}); + uint16_t deviceType = 0, const char * deviceName = {}, size_t numIPs = 0, + chip::Inet::IPAddress * ipAddressList = nullptr); CHIP_ERROR FindOrEstablishCASESession(std::function onConnectionSuccess, std::function onConnectionFailure); TargetEndpointInfo * GetOrAddEndpoint(chip::EndpointId endpointId); @@ -107,6 +111,8 @@ class TargetVideoPlayerInfo uint16_t mProductId = 0; uint16_t mDeviceType = 0; char mDeviceName[chip::Dnssd::kMaxDeviceNameLen + 1] = {}; + size_t mNumIPs = 0; // number of valid IP addresses + chip::Inet::IPAddress mIpAddress[chip::Dnssd::CommonResolutionData::kMaxIPAddresses]; chip::Callback::Callback mOnConnectedCallback; chip::Callback::Callback mOnConnectionFailureCallback; diff --git a/examples/tv-casting-app/tv-casting-common/src/AppParams.cpp b/examples/tv-casting-app/tv-casting-common/src/AppParams.cpp new file mode 100644 index 00000000000000..62a98feda4375f --- /dev/null +++ b/examples/tv-casting-app/tv-casting-common/src/AppParams.cpp @@ -0,0 +1,36 @@ +/* + * + * Copyright (c) 2022 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 "AppParams.h" + +#if CHIP_ENABLE_ROTATING_DEVICE_ID +AppParams::AppParams(chip::Optional rotatingDeviceIdUniqueId) +{ + mRotatingDeviceIdUniqueId = rotatingDeviceIdUniqueId; +} + +void AppParams::SetRotatingDeviceIdUniqueId(chip::Optional rotatingDeviceIdUniqueId) +{ + mRotatingDeviceIdUniqueId = rotatingDeviceIdUniqueId; +} + +chip::Optional AppParams::GetRotatingDeviceIdUniqueId() +{ + return mRotatingDeviceIdUniqueId; +} +#endif // CHIP_ENABLE_ROTATING_DEVICE_ID diff --git a/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp b/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp index 7891070728abfe..505f578086dd71 100644 --- a/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp +++ b/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp @@ -25,20 +25,7 @@ using namespace chip::app::Clusters::ContentLauncher::Commands; CastingServer * CastingServer::castingServer_ = nullptr; -CastingServer::CastingServer() -{ -#if CHIP_ENABLE_ROTATING_DEVICE_ID && defined(CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID) - // generate and set a random uniqueId for generating rotatingId - uint8_t rotatingDeviceIdUniqueId[chip::DeviceLayer::ConfigurationManager::kRotatingDeviceIDUniqueIDLength]; - for (size_t i = 0; i < sizeof(rotatingDeviceIdUniqueId); i++) - { - rotatingDeviceIdUniqueId[i] = chip::Crypto::GetRandU8(); - } - - ByteSpan rotatingDeviceIdUniqueIdSpan(rotatingDeviceIdUniqueId); - chip::DeviceLayer::ConfigurationMgr().SetRotatingDeviceIdUniqueId(rotatingDeviceIdUniqueIdSpan); -#endif // CHIP_ENABLE_ROTATING_DEVICE_ID && defined(CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID) -} +CastingServer::CastingServer() {} CastingServer * CastingServer::GetInstance() { @@ -49,20 +36,44 @@ CastingServer * CastingServer::GetInstance() return castingServer_; } -void CastingServer::Init() +CHIP_ERROR CastingServer::Init(AppParams * AppParams) { if (mInited) { - return; + return CHIP_NO_ERROR; + } + +#if CHIP_ENABLE_ROTATING_DEVICE_ID + // if this class's client provided a RotatingDeviceIdUniqueId, use that + if (AppParams != nullptr && AppParams->GetRotatingDeviceIdUniqueId().HasValue()) + { + ByteSpan rotatingDeviceIdUniqueId(AppParams->GetRotatingDeviceIdUniqueId().Value()); + chip::DeviceLayer::ConfigurationMgr().SetRotatingDeviceIdUniqueId(rotatingDeviceIdUniqueId); + } +#ifdef CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID + else + { + // otherwise, generate and set a random uniqueId for generating rotatingId + uint8_t rotatingDeviceIdUniqueId[chip::DeviceLayer::ConfigurationManager::kRotatingDeviceIDUniqueIDLength]; + for (size_t i = 0; i < sizeof(rotatingDeviceIdUniqueId); i++) + { + rotatingDeviceIdUniqueId[i] = chip::Crypto::GetRandU8(); + } + + // ByteSpan rotatingDeviceIdUniqueIdSpan(rotatingDeviceIdUniqueId); + chip::DeviceLayer::ConfigurationMgr().SetRotatingDeviceIdUniqueId(ByteSpan(rotatingDeviceIdUniqueId)); } +#endif // CHIP_DEVICE_CONFIG_ROTATING_DEVICE_ID_UNIQUE_ID +#endif // CHIP_ENABLE_ROTATING_DEVICE_ID // Initialize binding handlers - ReturnOnFailure(InitBindingHandlers()); + ReturnErrorOnFailure(InitBindingHandlers()); // Add callback to send Content casting commands after commissioning completes - ReturnOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(DeviceEventCallback, 0)); + ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(DeviceEventCallback, 0)); mInited = true; + return CHIP_NO_ERROR; } CHIP_ERROR CastingServer::InitBindingHandlers() @@ -88,6 +99,12 @@ CHIP_ERROR CastingServer::TargetVideoPlayerInfoInit(NodeId nodeId, FabricIndex f CHIP_ERROR CastingServer::DiscoverCommissioners() { + TargetVideoPlayerInfo * connectableVideoPlayerList = ReadCachedTargetVideoPlayerInfos(); + if (connectableVideoPlayerList == nullptr || !connectableVideoPlayerList[0].IsInitialized()) + { + ChipLogProgress(AppServer, "No cached video players found during discovery"); + } + // Send discover commissioners request return mCommissionableNodeController.DiscoverCommissioners( Dnssd::DiscoveryFilter(Dnssd::DiscoveryFilterType::kDeviceType, static_cast(35))); @@ -120,15 +137,32 @@ CHIP_ERROR CastingServer::SendUserDirectedCommissioningRequest(Dnssd::Discovered mTargetVideoPlayerVendorId = selectedCommissioner->commissionData.vendorId; mTargetVideoPlayerProductId = selectedCommissioner->commissionData.productId; mTargetVideoPlayerDeviceType = selectedCommissioner->commissionData.deviceType; + mTargetVideoPlayerNumIPs = selectedCommissioner->resolutionData.numIPs; + for (size_t i = 0; i < mTargetVideoPlayerNumIPs && i < chip::Dnssd::CommonResolutionData::kMaxIPAddresses; i++) + { + mTargetVideoPlayerIpAddress[i] = selectedCommissioner->resolutionData.ipAddress[i]; + } chip::Platform::CopyString(mTargetVideoPlayerDeviceName, chip::Dnssd::kMaxDeviceNameLen + 1, selectedCommissioner->commissionData.deviceName); return CHIP_NO_ERROR; } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT -const Dnssd::DiscoveredNodeData * CastingServer::GetDiscoveredCommissioner(int index) +const Dnssd::DiscoveredNodeData * +CastingServer::GetDiscoveredCommissioner(int index, chip::Optional & outAssociatedConnectableVideoPlayer) { - return mCommissionableNodeController.GetDiscoveredCommissioner(index); + const Dnssd::DiscoveredNodeData * discoveredNodeData = mCommissionableNodeController.GetDiscoveredCommissioner(index); + if (discoveredNodeData != nullptr) + { + for (size_t i = 0; i < kMaxCachedVideoPlayers && mCachedTargetVideoPlayerInfo[i].IsInitialized(); i++) + { + if (mCachedTargetVideoPlayerInfo[i].IsSameAs(discoveredNodeData)) + { + outAssociatedConnectableVideoPlayer = MakeOptional(&mCachedTargetVideoPlayerInfo[i]); + } + } + } + return discoveredNodeData; } void CastingServer::ReadServerClustersForNode(NodeId nodeId) @@ -301,7 +335,8 @@ void CastingServer::DeviceEventCallback(const DeviceLayer::ChipDeviceEvent * eve CastingServer::GetInstance()->mOnConnectionSuccessClientCallback, CastingServer::GetInstance()->mOnConnectionFailureClientCallback, CastingServer::GetInstance()->mTargetVideoPlayerVendorId, CastingServer::GetInstance()->mTargetVideoPlayerProductId, - CastingServer::GetInstance()->mTargetVideoPlayerDeviceType, CastingServer::GetInstance()->mTargetVideoPlayerDeviceName); + CastingServer::GetInstance()->mTargetVideoPlayerDeviceType, CastingServer::GetInstance()->mTargetVideoPlayerDeviceName, + CastingServer::GetInstance()->mTargetVideoPlayerNumIPs, CastingServer::GetInstance()->mTargetVideoPlayerIpAddress); CastingServer::GetInstance()->mCommissioningCompleteCallback(err); } @@ -823,6 +858,97 @@ CHIP_ERROR CastingServer::ApplicationBasic_SubscribeToAllowedVendorList( onSubscriptionEstablished); } +CHIP_ERROR CastingServer::ApplicationBasic_ReadVendorName( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::VendorName::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn) +{ + ReturnErrorOnFailure(mVendorNameReader.SetTarget(mActiveTargetVideoPlayerInfo, endpoint->GetEndpointId())); + return mVendorNameReader.ReadAttribute(context, successFn, failureFn); +} + +CHIP_ERROR +CastingServer::ApplicationBasic_ReadVendorID( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn) +{ + ReturnErrorOnFailure(mVendorIDReader.SetTarget(mActiveTargetVideoPlayerInfo, endpoint->GetEndpointId())); + return mVendorIDReader.ReadAttribute(context, successFn, failureFn); +} + +CHIP_ERROR CastingServer::ApplicationBasic_ReadApplicationName( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::ApplicationName::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn) +{ + ReturnErrorOnFailure(mApplicationNameReader.SetTarget(mActiveTargetVideoPlayerInfo, endpoint->GetEndpointId())); + return mApplicationNameReader.ReadAttribute(context, successFn, failureFn); +} + +CHIP_ERROR +CastingServer::ApplicationBasic_ReadProductID( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::ProductID::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn) +{ + ReturnErrorOnFailure(mProductIDReader.SetTarget(mActiveTargetVideoPlayerInfo, endpoint->GetEndpointId())); + return mProductIDReader.ReadAttribute(context, successFn, failureFn); +} + +CHIP_ERROR CastingServer::ApplicationBasic_ReadApplication( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::Application::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn) +{ + ReturnErrorOnFailure(mApplicationReader.SetTarget(mActiveTargetVideoPlayerInfo, endpoint->GetEndpointId())); + return mApplicationReader.ReadAttribute(context, successFn, failureFn); +} + +CHIP_ERROR +CastingServer::ApplicationBasic_ReadStatus( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::Status::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn) +{ + ReturnErrorOnFailure(mStatusReader.SetTarget(mActiveTargetVideoPlayerInfo, endpoint->GetEndpointId())); + return mStatusReader.ReadAttribute(context, successFn, failureFn); +} + +CHIP_ERROR CastingServer::ApplicationBasic_ReadApplicationVersion( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::ApplicationVersion::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn) +{ + ReturnErrorOnFailure(mApplicationVersionReader.SetTarget(mActiveTargetVideoPlayerInfo, endpoint->GetEndpointId())); + return mApplicationVersionReader.ReadAttribute(context, successFn, failureFn); +} + +CHIP_ERROR CastingServer::ApplicationBasic_ReadAllowedVendorList( + TargetEndpointInfo * endpoint, void * context, + chip::Controller::ReadResponseSuccessCallback< + chip::app::Clusters::ApplicationBasic::Attributes::AllowedVendorList::TypeInfo::DecodableArgType> + successFn, + chip::Controller::ReadResponseFailureCallback failureFn) +{ + ReturnErrorOnFailure(mAllowedVendorListReader.SetTarget(mActiveTargetVideoPlayerInfo, endpoint->GetEndpointId())); + return mAllowedVendorListReader.ReadAttribute(context, successFn, failureFn); +} + /* * @brief Channel cluster */ diff --git a/examples/tv-casting-app/tv-casting-common/src/PersistenceManager.cpp b/examples/tv-casting-app/tv-casting-common/src/PersistenceManager.cpp index 0d71d28ca83a38..a57415d68c7582 100644 --- a/examples/tv-casting-app/tv-casting-common/src/PersistenceManager.cpp +++ b/examples/tv-casting-app/tv-casting-common/src/PersistenceManager.cpp @@ -71,7 +71,7 @@ CHIP_ERROR PersistenceManager::WriteAllVideoPlayers(TargetVideoPlayerInfo videoP TLV::TLVType outerContainerType = TLV::kTLVType_Structure; ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainerType)); - ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(kCastingDataVersionTag), kCastingDataVersion)); + ReturnErrorOnFailure(tlvWriter.Put(TLV::ContextTag(kCurrentCastingDataVersionTag), kCurrentCastingDataVersion)); TLV::TLVType videoPlayersContainerType = TLV::kTLVType_Array; // Video Players container starts @@ -92,6 +92,27 @@ CHIP_ERROR PersistenceManager::WriteAllVideoPlayers(TargetVideoPlayerInfo videoP ReturnErrorOnFailure(tlvWriter.PutBytes(TLV::ContextTag(kVideoPlayerDeviceNameTag), (const uint8_t *) videoPlayer->GetDeviceName(), static_cast(strlen(videoPlayer->GetDeviceName()) + 1))); + ReturnErrorOnFailure( + tlvWriter.Put(TLV::ContextTag(kVideoPlayerNumIPsTag), static_cast(videoPlayer->GetNumIPs()))); + const Inet::IPAddress * ipAddress = videoPlayer->GetIpAddresses(); + if (ipAddress != nullptr && videoPlayer->GetNumIPs() > 0) + { + TLV::TLVType ipAddressesContainerType = TLV::kTLVType_Array; + // IP Addresses container starts + ReturnErrorOnFailure(tlvWriter.StartContainer(TLV::ContextTag(kIpAddressesContainerTag), TLV::kTLVType_Structure, + ipAddressesContainerType)); + for (size_t i = 0; i < videoPlayer->GetNumIPs() && i < chip::Dnssd::CommonResolutionData::kMaxIPAddresses; i++) + { + char ipAddressStr[Inet::IPAddress::kMaxStringLength]; + ipAddress[i].ToString(ipAddressStr, Inet::IPAddress::kMaxStringLength); + ReturnErrorOnFailure(tlvWriter.PutBytes(TLV::ContextTag(kVideoPlayerIPAddressTag), + (const uint8_t *) ipAddressStr, + static_cast(strlen(ipAddressStr) + 1))); + } + // IP Addresses container ends + ReturnErrorOnFailure(tlvWriter.EndContainer(ipAddressesContainerType)); + } + TargetEndpointInfo * endpoints = videoPlayer->GetEndpoints(); if (endpoints != nullptr) { @@ -132,7 +153,7 @@ CHIP_ERROR PersistenceManager::WriteAllVideoPlayers(TargetVideoPlayerInfo videoP ReturnErrorOnFailure(tlvWriter.Finalize()); ChipLogProgress(AppServer, "PersistenceManager::WriteAllVideoPlayers TLV(CastingData).LengthWritten: %d bytes and version: %d", - tlvWriter.GetLengthWritten(), kCastingDataVersion); + tlvWriter.GetLengthWritten(), kCurrentCastingDataVersion); return chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr().Put(kCastingDataKey, castingData, tlvWriter.GetLengthWritten()); } @@ -162,7 +183,7 @@ CHIP_ERROR PersistenceManager::ReadAllVideoPlayers(TargetVideoPlayerInfo outVide ReturnErrorOnFailure(reader.Next()); TLV::Tag outerContainerTag = reader.GetTag(); uint8_t outerContainerTagTagNum = static_cast(TLV::TagNumFromTag(outerContainerTag)); - VerifyOrReturnError(outerContainerTagTagNum == kCastingDataVersionTag, CHIP_ERROR_INVALID_TLV_TAG); + VerifyOrReturnError(outerContainerTagTagNum == kCurrentCastingDataVersionTag, CHIP_ERROR_INVALID_TLV_TAG); uint32_t version; ReturnErrorOnFailure(reader.Get(version)); ChipLogProgress(AppServer, "PersistenceManager::ReadAllVideoPlayers TLV(CastingData) version: %d", version); @@ -178,6 +199,8 @@ CHIP_ERROR PersistenceManager::ReadAllVideoPlayers(TargetVideoPlayerInfo outVide uint16_t productId = 0; uint16_t deviceType = 0; char deviceName[chip::Dnssd::kMaxDeviceNameLen + 1] = {}; + size_t numIPs = 0; + Inet::IPAddress ipAddress[chip::Dnssd::CommonResolutionData::kMaxIPAddresses]; CHIP_ERROR err; while ((err = reader.Next()) == CHIP_NO_ERROR) { @@ -188,47 +211,91 @@ CHIP_ERROR PersistenceManager::ReadAllVideoPlayers(TargetVideoPlayerInfo outVide return CHIP_ERROR_INVALID_TLV_TAG; } - uint8_t viewPlayersContainerTagNum = static_cast(TLV::TagNumFromTag(videoPlayersContainerTag)); - if (viewPlayersContainerTagNum == kNodeIdTag) + uint8_t videoPlayersContainerTagNum = static_cast(TLV::TagNumFromTag(videoPlayersContainerTag)); + if (videoPlayersContainerTagNum == kNodeIdTag) { ReturnErrorOnFailure(reader.Get(nodeId)); continue; } - if (viewPlayersContainerTagNum == kFabricIndexTag) + if (videoPlayersContainerTagNum == kFabricIndexTag) { ReturnErrorOnFailure(reader.Get(fabricIndex)); continue; } - if (viewPlayersContainerTagNum == kVideoPlayerVendorIdTag) + if (videoPlayersContainerTagNum == kVideoPlayerVendorIdTag) { ReturnErrorOnFailure(reader.Get(vendorId)); continue; } - if (viewPlayersContainerTagNum == kVideoPlayerProductIdTag) + if (videoPlayersContainerTagNum == kVideoPlayerProductIdTag) { ReturnErrorOnFailure(reader.Get(productId)); continue; } - if (viewPlayersContainerTagNum == kVideoPlayerDeviceTypeIdTag) + if (videoPlayersContainerTagNum == kVideoPlayerDeviceTypeIdTag) { ReturnErrorOnFailure(reader.Get(deviceType)); continue; } - if (viewPlayersContainerTagNum == kVideoPlayerDeviceNameTag) + if (videoPlayersContainerTagNum == kVideoPlayerDeviceNameTag) { ReturnErrorOnFailure(reader.GetBytes(reinterpret_cast(deviceName), chip::Dnssd::kMaxDeviceNameLen + 1)); continue; } - if (viewPlayersContainerTagNum == kContentAppEndpointsContainerTag) + if (videoPlayersContainerTagNum == kVideoPlayerNumIPsTag) + { + ReturnErrorOnFailure(reader.Get(reinterpret_cast(numIPs))); + continue; + } + + if (videoPlayersContainerTagNum == kIpAddressesContainerTag) + { + // Entering IP Addresses container + TLV::TLVType ipAddressesContainerType = TLV::kTLVType_Array; + ReturnErrorOnFailure(reader.EnterContainer(ipAddressesContainerType)); + + size_t ipCount = 0; + while ((err = reader.Next()) == CHIP_NO_ERROR) + { + TLV::Tag ipAddressesContainerTag = reader.GetTag(); + if (!TLV::IsContextTag(ipAddressesContainerTag)) + { + ChipLogError(AppServer, "Unexpected non-context TLV tag."); + return CHIP_ERROR_INVALID_TLV_TAG; + } + + uint8_t ipAddressesContainerTagNum = static_cast(TLV::TagNumFromTag(ipAddressesContainerTag)); + if (ipAddressesContainerTagNum == kVideoPlayerIPAddressTag) + { + char ipAddressStr[Inet::IPAddress::kMaxStringLength]; + ReturnErrorOnFailure( + reader.GetBytes(reinterpret_cast(ipAddressStr), Inet::IPAddress::kMaxStringLength)); + + Inet::IPAddress addressInet; + VerifyOrReturnError(Inet::IPAddress::FromString(ipAddressStr, addressInet), CHIP_ERROR_INVALID_TLV_ELEMENT); + ipAddress[ipCount] = addressInet; + ipCount++; + continue; + } + } + if (err == CHIP_END_OF_TLV) + { + // Exiting IP Addresses container + ReturnErrorOnFailure(reader.ExitContainer(ipAddressesContainerType)); + continue; + } + } + + if (videoPlayersContainerTagNum == kContentAppEndpointsContainerTag) { outVideoPlayers[videoPlayerIndex].Initialize(nodeId, fabricIndex, nullptr, nullptr, vendorId, productId, deviceType, - deviceName); + deviceName, numIPs, ipAddress); // Entering Content App Endpoints container TLV::TLVType contentAppEndpointArrayContainerType = TLV::kTLVType_Array; ReturnErrorOnFailure(reader.EnterContainer(contentAppEndpointArrayContainerType)); diff --git a/examples/tv-casting-app/tv-casting-common/src/TargetVideoPlayerInfo.cpp b/examples/tv-casting-app/tv-casting-common/src/TargetVideoPlayerInfo.cpp index ff85e20eaaa0dd..70609f328fc5ef 100644 --- a/examples/tv-casting-app/tv-casting-common/src/TargetVideoPlayerInfo.cpp +++ b/examples/tv-casting-app/tv-casting-common/src/TargetVideoPlayerInfo.cpp @@ -24,7 +24,8 @@ CASEClientPool gCASEClientPool; CHIP_ERROR TargetVideoPlayerInfo::Initialize(NodeId nodeId, FabricIndex fabricIndex, std::function onConnectionSuccess, std::function onConnectionFailure, uint16_t vendorId, - uint16_t productId, uint16_t deviceType, const char * deviceName) + uint16_t productId, uint16_t deviceType, const char * deviceName, size_t numIPs, + chip::Inet::IPAddress * ipAddress) { ChipLogProgress(NotSpecified, "TargetVideoPlayerInfo nodeId=0x" ChipLogFormatX64 " fabricIndex=%d", ChipLogValueX64(nodeId), fabricIndex); @@ -33,6 +34,12 @@ CHIP_ERROR TargetVideoPlayerInfo::Initialize(NodeId nodeId, FabricIndex fabricIn mVendorId = vendorId; mProductId = productId; mDeviceType = deviceType; + mNumIPs = numIPs; + for (size_t i = 0; i < numIPs && i < chip::Dnssd::CommonResolutionData::kMaxIPAddresses; i++) + { + mIpAddress[i] = ipAddress[i]; + } + chip::Platform::CopyString(mDeviceName, chip::Dnssd::kMaxDeviceNameLen + 1, deviceName); for (auto & endpointInfo : mEndpoints) { @@ -118,3 +125,47 @@ void TargetVideoPlayerInfo::PrintInfo() } } } + +bool TargetVideoPlayerInfo::IsSameAs(const chip::Dnssd::DiscoveredNodeData * discoveredNodeData) +{ + // return false because 'this' VideoPlayer is not null + if (discoveredNodeData == nullptr) + { + return false; + } + + // return false because deviceNames are different + if (strcmp(mDeviceName, discoveredNodeData->commissionData.deviceName) != 0) + { + return false; + } + + // return false because not even a single IP Address matches + if (mNumIPs > 0) + { + bool matchFound = false; + for (size_t i = 0; i < mNumIPs && i < chip::Dnssd::CommonResolutionData::kMaxIPAddresses; i++) + { + for (size_t j = 0; + j < discoveredNodeData->resolutionData.numIPs && j < chip::Dnssd::CommonResolutionData::kMaxIPAddresses; j++) + { + if (mIpAddress[i] == discoveredNodeData->resolutionData.ipAddress[j]) + { + matchFound = true; + break; + } + } + if (matchFound) + { + break; + } + } + + if (!matchFound) + { + return false; + } + } + + return true; +}