TIAF: add signing request and result

Detailed call sequence can be found at
go/tiaf-use-cases

And add INTENT_KEY_COMMAND_TYPE

Bug: 224121339
Bug: 225082750
Test: atest testRequestSigning
Test: atest testSendSigningResult

Change-Id: I32b7e77dc7fc357ce168f486c28c16863bbbb3bf
This commit is contained in:
shubang
2022-03-15 16:21:30 -07:00
committed by Shubang Lu
parent a341f3003a
commit 553d6fb7c5
9 changed files with 255 additions and 2 deletions

View File

@@ -26135,6 +26135,7 @@ package android.media.tv.interactive {
field public static final String INTENT_KEY_BI_INTERACTIVE_APP_TYPE = "bi_interactive_app_type";
field public static final String INTENT_KEY_BI_INTERACTIVE_APP_URI = "bi_interactive_app_uri";
field public static final String INTENT_KEY_CHANNEL_URI = "channel_uri";
field public static final String INTENT_KEY_COMMAND_TYPE = "command_type";
field public static final String INTENT_KEY_INTERACTIVE_APP_SERVICE_ID = "interactive_app_id";
field public static final String INTENT_KEY_TV_INPUT_ID = "tv_input_id";
field public static final int INTERACTIVE_APP_STATE_ERROR = 3; // 0x3
@@ -26209,6 +26210,7 @@ package android.media.tv.interactive {
method public abstract boolean onSetSurface(@Nullable android.view.Surface);
method public void onSetTeletextAppEnabled(boolean);
method public void onSignalStrength(int);
method public void onSigningResult(@NonNull String, @NonNull byte[]);
method public void onStartInteractiveApp();
method public void onStopInteractiveApp();
method public void onStreamVolume(float);
@@ -26227,6 +26229,7 @@ package android.media.tv.interactive {
method @CallSuper public void requestCurrentChannelLcn();
method @CallSuper public void requestCurrentChannelUri();
method @CallSuper public void requestCurrentTvInputId();
method @CallSuper public void requestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
method @CallSuper public void requestStreamVolume();
method @CallSuper public void requestTrackInfoList();
method @CallSuper public void sendPlaybackCommandRequest(@NonNull String, @Nullable android.os.Bundle);
@@ -26268,6 +26271,7 @@ package android.media.tv.interactive {
method public void sendCurrentChannelLcn(int);
method public void sendCurrentChannelUri(@Nullable android.net.Uri);
method public void sendCurrentTvInputId(@Nullable String);
method public void sendSigningResult(@NonNull String, @NonNull byte[]);
method public void sendStreamVolume(float);
method public void sendTrackInfoList(@Nullable java.util.List<android.media.tv.TvTrackInfo>);
method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback);
@@ -26276,6 +26280,9 @@ package android.media.tv.interactive {
method public int setTvView(@Nullable android.media.tv.TvView);
method public void startInteractiveApp();
method public void stopInteractiveApp();
field public static final String BI_INTERACTIVE_APP_KEY_ALIAS = "alias";
field public static final String BI_INTERACTIVE_APP_KEY_CERTIFICATE = "certificate";
field public static final String BI_INTERACTIVE_APP_KEY_PRIVATE_KEY = "private_key";
}
public static interface TvInteractiveAppView.OnUnhandledInputEventListener {
@@ -26289,6 +26296,7 @@ package android.media.tv.interactive {
method public void onRequestCurrentChannelLcn(@NonNull String);
method public void onRequestCurrentChannelUri(@NonNull String);
method public void onRequestCurrentTvInputId(@NonNull String);
method public void onRequestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
method public void onRequestStreamVolume(@NonNull String);
method public void onRequestTrackInfoList(@NonNull String);
method public void onSetVideoBounds(@NonNull String, @NonNull android.graphics.Rect);

View File

@@ -44,5 +44,7 @@ oneway interface ITvInteractiveAppClient {
void onRequestStreamVolume(int seq);
void onRequestTrackInfoList(int seq);
void onRequestCurrentTvInputId(int seq);
void onRequestSigning(
in String id, in String algorithm, in String alias, in byte[] data, int seq);
void onAdRequest(in AdRequest request, int Seq);
}

View File

@@ -50,6 +50,8 @@ interface ITvInteractiveAppManager {
void sendStreamVolume(in IBinder sessionToken, float volume, int userId);
void sendTrackInfoList(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId);
void sendCurrentTvInputId(in IBinder sessionToken, in String inputId, int userId);
void sendSigningResult(in IBinder sessionToken, in String signingId, in byte[] result,
int userId);
void createSession(in ITvInteractiveAppClient client, in String iAppServiceId, int type,
int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);

View File

@@ -42,6 +42,7 @@ oneway interface ITvInteractiveAppSession {
void sendStreamVolume(float volume);
void sendTrackInfoList(in List<TvTrackInfo> tracks);
void sendCurrentTvInputId(in String inputId);
void sendSigningResult(in String signingId, in byte[] result);
void release();
void notifyTuned(in Uri channelUri);
void notifyTrackSelected(int type, in String trackId);

View File

@@ -43,5 +43,6 @@ oneway interface ITvInteractiveAppSessionCallback {
void onRequestStreamVolume();
void onRequestTrackInfoList();
void onRequestCurrentTvInputId();
void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data);
void onAdRequest(in AdRequest request);
}

View File

@@ -249,7 +249,7 @@ public final class TvInteractiveAppManager {
*
* @see #sendAppLinkCommand(String, Bundle)
* @see #ACTION_APP_LINK_COMMAND
* @see android.media.tv.interactive.TvInteractiveAppServiceInfo#getId()
* @see TvInteractiveAppServiceInfo#getId()
*/
public static final String INTENT_KEY_INTERACTIVE_APP_SERVICE_ID = "interactive_app_id";
@@ -285,6 +285,16 @@ public final class TvInteractiveAppManager {
*/
public static final String INTENT_KEY_BI_INTERACTIVE_APP_URI = "bi_interactive_app_uri";
/**
* Intent key for command type. It's used to send app command to TV app. The value of this key
* could vary according to TV apps.
* <p>Type: String
*
* @see #sendAppLinkCommand(String, Bundle)
* @see #ACTION_APP_LINK_COMMAND
*/
public static final String INTENT_KEY_COMMAND_TYPE = "command_type";
private final ITvInteractiveAppManager mService;
private final int mUserId;
@@ -477,6 +487,19 @@ public final class TvInteractiveAppManager {
}
}
@Override
public void onRequestSigning(
String id, String algorithm, String alias, byte[] data, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
Log.e(TAG, "Callback not found for seq " + seq);
return;
}
record.postRequestSigning(id, algorithm, alias, data);
}
}
@Override
public void onSessionStateChanged(int state, int err, int seq) {
synchronized (mSessionCallbackRecordMap) {
@@ -1024,6 +1047,18 @@ public final class TvInteractiveAppManager {
}
}
void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.sendSigningResult(mToken, signingId, result, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets the {@link android.view.Surface} for this session.
*
@@ -1657,6 +1692,15 @@ public final class TvInteractiveAppManager {
});
}
void postRequestSigning(String id, String algorithm, String alias, byte[] data) {
mHandler.post(new Runnable() {
@Override
public void run() {
mSessionCallback.onRequestSigning(mSession, id, algorithm, alias, data);
}
});
}
void postAdRequest(final AdRequest request) {
mHandler.post(new Runnable() {
@Override
@@ -1794,11 +1838,26 @@ public final class TvInteractiveAppManager {
* called.
*
* @param session A {@link TvInteractiveAppService.Session} associated with this callback.
* @hide
*/
public void onRequestCurrentTvInputId(Session session) {
}
/**
* This is called when
* {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is
* called.
*
* @param session A {@link TvInteractiveAppService.Session} associated with this callback.
* @param signingId the ID to identify the request.
* @param algorithm the standard name of the signature algorithm requested, such as
* MD5withRSA, SHA256withDSA, etc.
* @param alias the alias of the corresponding {@link java.security.KeyStore}.
* @param data the original bytes to be signed.
*/
public void onRequestSigning(
Session session, String signingId, String algorithm, String alias, byte[] data) {
}
/**
* This is called when {@link TvInteractiveAppService.Session#notifySessionStateChanged} is
* called.

View File

@@ -451,6 +451,17 @@ public abstract class TvInteractiveAppService extends Service {
public void onCurrentTvInputId(@Nullable String inputId) {
}
/**
* Receives signing result.
* @param signingId the ID to identify the request. It's the same as the corresponding ID in
* {@link Session#requestSigning(String, String, String, byte[])}
* @param result the signed result.
*
* @see #requestSigning(String, String, String, byte[])
*/
public void onSigningResult(@NonNull String signingId, @NonNull byte[] result) {
}
/**
* Called when the application sets the surface.
*
@@ -876,6 +887,47 @@ public abstract class TvInteractiveAppService extends Service {
});
}
/**
* Requests signing of the given data.
*
* <p>This is used when the corresponding server of the broadcast-independent interactive
* app requires signing during handshaking, and the interactive app service doesn't have
* the built-in private key. The private key is provided by the content providers and
* pre-built in the related app, such as TV app.
*
* @param signingId the ID to identify the request. When a result is received, this ID can
* be used to correlate the result with the request.
* @param algorithm the standard name of the signature algorithm requested, such as
* MD5withRSA, SHA256withDSA, etc. The name is from standards like
* FIPS PUB 186-4 and PKCS #1.
* @param alias the alias of the corresponding {@link java.security.KeyStore}.
* @param data the original bytes to be signed.
*
* @see #onSigningResult(String, byte[])
* @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle)
* @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS
*/
@CallSuper
public void requestSigning(@NonNull String signingId, @NonNull String algorithm,
@NonNull String alias, @NonNull byte[] data) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
public void run() {
try {
if (DEBUG) {
Log.d(TAG, "requestSigning");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestSigning(signingId, algorithm, alias, data);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestSigning", e);
}
}
});
}
/**
* Sends an advertisement request to be processed by the related TV input.
*
@@ -945,6 +997,10 @@ public abstract class TvInteractiveAppService extends Service {
onCurrentTvInputId(inputId);
}
void sendSigningResult(String signingId, byte[] result) {
onSigningResult(signingId, result);
}
void release() {
onRelease();
if (mSurface != null) {
@@ -1412,6 +1468,11 @@ public abstract class TvInteractiveAppService extends Service {
mSessionImpl.sendCurrentTvInputId(inputId);
}
@Override
public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
mSessionImpl.sendSigningResult(signingId, result);
}
@Override
public void release() {
mSessionImpl.scheduleMediaViewCleanup();

View File

@@ -46,6 +46,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import java.security.KeyStore;
import java.util.List;
import java.util.concurrent.Executor;
@@ -61,6 +62,28 @@ public class TvInteractiveAppView extends ViewGroup {
private static final int UNSET_TVVIEW_SUCCESS = 3;
private static final int UNSET_TVVIEW_FAIL = 4;
/**
* Used to share client {@link java.security.cert.Certificate} with
* {@link TvInteractiveAppService}.
* @see #createBiInteractiveApp(Uri, Bundle)
* @see java.security.cert.Certificate
*/
public static final String BI_INTERACTIVE_APP_KEY_CERTIFICATE = "certificate";
/**
* Used to share the {@link KeyStore} alias with {@link TvInteractiveAppService}.
* @see #createBiInteractiveApp(Uri, Bundle)
* @see KeyStore#aliases()
*/
public static final String BI_INTERACTIVE_APP_KEY_ALIAS = "alias";
/**
* Used to share the {@link java.security.PrivateKey} with {@link TvInteractiveAppService}.
* <p>The private key is optional. It is used to encrypt data when necessary.
*
* @see #createBiInteractiveApp(Uri, Bundle)
* @see java.security.PrivateKey
*/
public static final String BI_INTERACTIVE_APP_KEY_PRIVATE_KEY = "private_key";
private final TvInteractiveAppManager mTvInteractiveAppManager;
private final Handler mHandler = new Handler();
private final Object mCallbackLock = new Object();
@@ -513,6 +536,27 @@ public class TvInteractiveAppView extends ViewGroup {
}
}
/**
* Sends signing result to related TV interactive app.
*
* <p>This is used when the corresponding server of the broadcast-independent interactive
* app requires signing during handshaking, and the interactive app service doesn't have
* the built-in private key. The private key is provided by the content providers and
* pre-built in the related app, such as TV app.
*
* @param signingId the ID to identify the request. It's the same as the corresponding ID in
* {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])}
* @param result the signed result.
*/
public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
if (DEBUG) {
Log.d(TAG, "sendSigningResult");
}
if (mSession != null) {
mSession.sendSigningResult(signingId, result);
}
}
private void resetInternal() {
mSessionCallback = null;
if (mSession != null) {
@@ -725,6 +769,22 @@ public class TvInteractiveAppView extends ViewGroup {
public void onRequestCurrentTvInputId(@NonNull String iAppServiceId) {
}
/**
* This is called when
* {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is
* called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param signingId the ID to identify the request.
* @param algorithm the standard name of the signature algorithm requested, such as
* MD5withRSA, SHA256withDSA, etc.
* @param alias the alias of the corresponding {@link java.security.KeyStore}.
* @param data the original bytes to be signed.
*/
public void onRequestSigning(@NonNull String iAppServiceId, @NonNull String signingId,
@NonNull String algorithm, @NonNull String alias, @NonNull byte[] data) {
}
}
/**
@@ -1031,5 +1091,20 @@ public class TvInteractiveAppView extends ViewGroup {
mCallback.onRequestCurrentTvInputId(mIAppServiceId);
}
}
@Override
public void onRequestSigning(
Session session, String id, String algorithm, String alias, byte[] data) {
if (DEBUG) {
Log.d(TAG, "onRequestSigning");
}
if (this != mSessionCallback) {
Log.w(TAG, "onRequestSigning - session not created");
return;
}
if (mCallback != null) {
mCallback.onRequestSigning(mIAppServiceId, id, algorithm, alias, data);
}
}
}
}

View File

@@ -1359,6 +1359,32 @@ public class TvInteractiveAppManagerService extends SystemService {
}
}
@Override
public void sendSigningResult(
IBinder sessionToken, String signingId, byte[] result, int userId) {
if (DEBUG) {
Slogf.d(TAG, "sendSigningResult(signingId=%s)", signingId);
}
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "sendSigningResult");
SessionState sessionState = null;
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
getSessionLocked(sessionState).sendSigningResult(signingId, result);
} catch (RemoteException | SessionNotFoundException e) {
Slogf.e(TAG, "error in sendSigningResult", e);
}
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void setSurface(IBinder sessionToken, Surface surface, int userId) {
final int callingUid = Binder.getCallingUid();
@@ -2220,6 +2246,24 @@ public class TvInteractiveAppManagerService extends SystemService {
}
}
@Override
public void onRequestSigning(String id, String algorithm, String alias, byte[] data) {
synchronized (mLock) {
if (DEBUG) {
Slogf.d(TAG, "onRequestSigning");
}
if (mSessionState.mSession == null || mSessionState.mClient == null) {
return;
}
try {
mSessionState.mClient.onRequestSigning(
id, algorithm, alias, data, mSessionState.mSeq);
} catch (RemoteException e) {
Slogf.e(TAG, "error in onRequestSigning", e);
}
}
}
@Override
public void onAdRequest(AdRequest request) {
synchronized (mLock) {