From 19a1dd02e54f1fd49d1278e35fdd54c226ad44be Mon Sep 17 00:00:00 2001 From: shubang Date: Wed, 15 Dec 2021 04:53:39 -0800 Subject: [PATCH] TIAF: create & destroy BI IApp Bug: 211886434 Test: mmm Change-Id: Ie2b4d1d34b57487318f18a6fcdda116a05f9547f --- .../media/tv/interactive/ITvIAppClient.aidl | 2 + .../media/tv/interactive/ITvIAppManager.aidl | 3 + .../media/tv/interactive/ITvIAppSession.aidl | 6 +- .../interactive/ITvIAppSessionCallback.aidl | 2 + .../media/tv/interactive/TvIAppManager.java | 59 ++++++++++++- .../media/tv/interactive/TvIAppService.java | 66 ++++++++++++++ .../media/tv/interactive/TvIAppView.java | 85 +++++++++++++++++++ .../tv/interactive/TvIAppManagerService.java | 80 ++++++++++++++++- 8 files changed, 298 insertions(+), 5 deletions(-) diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl index a5f23315956dc..d6e37a653a294 100644 --- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl @@ -17,6 +17,7 @@ package android.media.tv.interactive; import android.media.tv.BroadcastInfoRequest; +import android.net.Uri; import android.os.Bundle; import android.view.InputChannel; @@ -31,5 +32,6 @@ oneway interface ITvIAppClient { void onLayoutSurface(int left, int top, int right, int bottom, int seq); void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq); void onSessionStateChanged(int state, int seq); + void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq); void onCommandRequest(in String cmdType, in Bundle parameters, int seq); } diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl index 40d8034888d92..e26c2abf949b0 100644 --- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl @@ -35,6 +35,9 @@ interface ITvIAppManager { void notifyAppLinkInfo(String tiasId, in Bundle info, int userId); void sendAppLinkCommand(String tiasId, in Bundle command, int userId); void startIApp(in IBinder sessionToken, int userId); + void createBiInteractiveApp( + in IBinder sessionToken, in Uri biIAppUri, in Bundle params, int userId); + void destroyBiInteractiveApp(in IBinder sessionToken, in String biIAppId, int userId); void createSession( in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId); void releaseSession(in IBinder sessionToken, int userId); diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl index 0d37a2baa49d9..78930c0d02fb5 100644 --- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl @@ -17,10 +17,10 @@ package android.media.tv.interactive; import android.graphics.Rect; +import android.media.tv.BroadcastInfoResponse; import android.net.Uri; -import android.media.tv.BroadcastInfoResponse; +import android.os.Bundle; import android.view.Surface; -import android.media.tv.BroadcastInfoResponse; /** * Sub-interface of ITvIAppService.aidl which is created per session and has its own context. @@ -28,6 +28,8 @@ import android.media.tv.BroadcastInfoResponse; */ oneway interface ITvIAppSession { void startIApp(); + void createBiInteractiveApp(in Uri biIAppUri, in Bundle params); + void destroyBiInteractiveApp(in String biIAppId); void release(); void notifyTuned(in Uri channelUri); void setSurface(in Surface surface); diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl index 66f5fc1d54aa2..c754c8c170a65 100644 --- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl @@ -19,6 +19,7 @@ package android.media.tv.interactive; import android.media.tv.BroadcastInfoRequest; import android.media.tv.interactive.ITvIAppSession; import android.media.tv.BroadcastInfoRequest; +import android.net.Uri; import android.os.Bundle; /** @@ -31,5 +32,6 @@ oneway interface ITvIAppSessionCallback { void onLayoutSurface(int left, int top, int right, int bottom); void onBroadcastInfoRequest(in BroadcastInfoRequest request); void onSessionStateChanged(int state); + void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId); void onCommandRequest(in String cmdType, in Bundle parameters); } diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java index 9390d8db08fb4..e9bbf059cf342 100644 --- a/media/java/android/media/tv/interactive/TvIAppManager.java +++ b/media/java/android/media/tv/interactive/TvIAppManager.java @@ -267,6 +267,18 @@ public final class TvIAppManager { record.postSessionStateChanged(state); } } + + @Override + public void onBiInteractiveAppCreated(Uri biIAppUri, String biIAppId, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postBiInteractiveAppCreated(biIAppUri, biIAppId); + } + } }; ITvIAppManagerCallback managerCallback = new ITvIAppManagerCallback.Stub() { @Override @@ -375,7 +387,6 @@ public final class TvIAppManager { public void onTvIAppInfoUpdated(TvIAppInfo iAppInfo) { } - /** * This is called when the state of the interactive app service is changed. * @hide @@ -622,6 +633,30 @@ public final class TvIAppManager { } } + void createBiInteractiveApp(Uri biIAppUri, Bundle params) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.createBiInteractiveApp(mToken, biIAppUri, params, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void destroyBiInteractiveApp(String biIAppId) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.destroyBiInteractiveApp(mToken, biIAppId, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Sets the {@link android.view.Surface} for this session. * @@ -1077,6 +1112,15 @@ public final class TvIAppManager { } }); } + + void postBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onBiInteractiveAppCreated(mSession, biIAppUri, biIAppId); + } + }); + } } /** @@ -1134,5 +1178,18 @@ public final class TvIAppManager { */ public void onSessionStateChanged(Session session, int state) { } + + /** + * This is called when {@link TvIAppService.Session#notifyBiInteractiveAppCreated} is + * called. + * + * @param session A {@link TvIAppManager.Session} associated with this callback. + * @param biIAppUri URI associated this BI interactive app. This is the same URI in + * {@link Session#createBiInteractiveApp(Uri, Bundle)} + * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive + * app. + */ + public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) { + } } } diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java index 6475f903f257b..2e7786cff5242 100644 --- a/media/java/android/media/tv/interactive/TvIAppService.java +++ b/media/java/android/media/tv/interactive/TvIAppService.java @@ -287,6 +287,28 @@ public abstract class TvIAppService extends Service { public void onStartIApp() { } + /** + * Creates broadcast-independent(BI) interactive application. + * + * @see #onDestroyBiInteractiveApp(String) + * @hide + */ + public void onCreateBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { + } + + + /** + * Destroys broadcast-independent(BI) interactive application. + * + * @param biIAppId the BI interactive app ID from + * {@link #createBiInteractiveApp(Uri, Bundle)} + * + * @see #onCreateBiInteractiveApp(Uri, Bundle) + * @hide + */ + public void onDestroyBiInteractiveApp(@NonNull String biIAppId) { + } + /** * Called when the application sets the surface. * @@ -497,6 +519,14 @@ public abstract class TvIAppService extends Service { onStartIApp(); } + void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { + onCreateBiInteractiveApp(biIAppUri, params); + } + + void destroyBiInteractiveApp(@NonNull String biIAppId) { + onDestroyBiInteractiveApp(biIAppId); + } + void release() { onRelease(); if (mSurface != null) { @@ -555,6 +585,32 @@ public abstract class TvIAppService extends Service { }); } + /** + * Notifies the broadcast-independent(BI) interactive application has been created. + * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive + * app. + * @hide + */ + public final void notifyBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "notifyBiInteractiveAppCreated (biIAppId=" + + biIAppId + ")"); + } + if (mSessionCallback != null) { + mSessionCallback.onBiInteractiveAppCreated(biIAppUri, biIAppId); + } + } catch (RemoteException e) { + Log.w(TAG, "error in notifyBiInteractiveAppCreated", e); + } + } + }); + } + /** * Takes care of dispatching incoming input events and tells whether the event was handled. */ @@ -792,6 +848,16 @@ public abstract class TvIAppService extends Service { mSessionImpl.startIApp(); } + @Override + public void createBiInteractiveApp(Uri biIAppUri, Bundle params) { + mSessionImpl.createBiInteractiveApp(biIAppUri, params); + } + + @Override + public void destroyBiInteractiveApp(String biIAppId) { + mSessionImpl.destroyBiInteractiveApp(biIAppId); + } + @Override public void release() { mSessionImpl.scheduleMediaViewCleanup(); diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java index efbe9e3bcf4bc..54d4eecc51692 100644 --- a/media/java/android/media/tv/interactive/TvIAppView.java +++ b/media/java/android/media/tv/interactive/TvIAppView.java @@ -16,6 +16,7 @@ package android.media.tv.interactive; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; @@ -26,6 +27,7 @@ import android.media.tv.TvInputManager; import android.media.tv.TvView; import android.media.tv.interactive.TvIAppManager.Session; import android.media.tv.interactive.TvIAppManager.SessionCallback; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.util.AttributeSet; @@ -301,6 +303,38 @@ public class TvIAppView extends ViewGroup { } } + /** + * Creates broadcast-independent(BI) interactive application. + * + * @see #destroyBiInteractiveApp(String) + * @hide + */ + public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { + if (DEBUG) { + Log.d(TAG, "createBiInteractiveApp Uri=" + biIAppUri + ", params=" + params); + } + if (mSession != null) { + mSession.createBiInteractiveApp(biIAppUri, params); + } + } + + /** + * Destroys broadcast-independent(BI) interactive application. + * + * @param biIAppId the BI interactive app ID from {@link #createBiInteractiveApp(Uri, Bundle)} + * + * @see #createBiInteractiveApp(Uri, Bundle) + * @hide + */ + public void destroyBiInteractiveApp(@NonNull String biIAppId) { + if (DEBUG) { + Log.d(TAG, "destroyBiInteractiveApp biIAppId=" + biIAppId); + } + if (mSession != null) { + mSession.destroyBiInteractiveApp(biIAppId); + } + } + public Session getIAppSession() { return mSession; } @@ -349,6 +383,28 @@ public class TvIAppView extends ViewGroup { public void onCommandRequest(String iAppServiceId, @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) { } + + /** + * This is called when the session state is changed. + * + * @param iAppServiceId The ID of the TV interactive app service bound to this view. + * @param state current session state. + */ + public void onSessionStateChanged(String iAppServiceId, int state) { + } + + /** + * This is called when broadcast-independent (BI) interactive app is created. + * + * @param iAppServiceId The ID of the TV interactive app service bound to this view. + * @param biIAppUri URI associated this BI interactive app. This is the same URI in + * {@link Session#createBiInteractiveApp(Uri, Bundle)} + * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive + * app. + */ + public void onBiInteractiveAppCreated(String iAppServiceId, Uri biIAppUri, + String biIAppId) { + } } private class MySessionCallback extends SessionCallback { @@ -440,5 +496,34 @@ public class TvIAppView extends ViewGroup { mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters); } } + + @Override + public void onSessionStateChanged(Session session, int state) { + if (DEBUG) { + Log.d(TAG, "onSessionStateChanged (state=" + state + ")"); + } + if (this != mSessionCallback) { + Log.w(TAG, "onSessionStateChanged - session not created"); + return; + } + if (mCallback != null) { + mCallback.onSessionStateChanged(mIAppServiceId, state); + } + } + + @Override + public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) { + if (DEBUG) { + Log.d(TAG, "onBiInteractiveAppCreated (biIAppUri=" + biIAppUri + ", biIAppId=" + + biIAppId + ")"); + } + if (this != mSessionCallback) { + Log.w(TAG, "onBiInteractiveAppCreated - session not created"); + return; + } + if (mCallback != null) { + mCallback.onBiInteractiveAppCreated(mIAppServiceId, biIAppUri, biIAppId); + } + } } } diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java index d70f970958164..7aa2dbd513898 100644 --- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java @@ -652,11 +652,13 @@ public class TvIAppManagerService extends SystemService { serviceState = new ServiceState( componentName, tiasId, resolvedUserId, true, type); userState.mServiceStateMap.put(componentName, serviceState); + updateServiceConnectionLocked(componentName, resolvedUserId); } else if (serviceState.mService != null) { serviceState.mService.prepare(type); } else { serviceState.mPendingPrepare = true; serviceState.mPendingPrepareType = type; + updateServiceConnectionLocked(componentName, resolvedUserId); } } } catch (RemoteException e) { @@ -687,10 +689,12 @@ public class TvIAppManagerService extends SystemService { componentName, tiasId, resolvedUserId); serviceState.addPendingAppLink(appLinkInfo); userState.mServiceStateMap.put(componentName, serviceState); + updateServiceConnectionLocked(componentName, resolvedUserId); } else if (serviceState.mService != null) { serviceState.mService.notifyAppLinkInfo(appLinkInfo); } else { serviceState.addPendingAppLink(appLinkInfo); + updateServiceConnectionLocked(componentName, resolvedUserId); } } } catch (RemoteException e) { @@ -850,7 +854,7 @@ public class TvIAppManagerService extends SystemService { } final int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, - userId, "notifyTuned"); + userId, "startIApp"); SessionState sessionState = null; final long identity = Binder.clearCallingIdentity(); try { @@ -868,6 +872,58 @@ public class TvIAppManagerService extends SystemService { } } + @Override + public void createBiInteractiveApp( + IBinder sessionToken, Uri biIAppUri, Bundle params, int userId) { + if (DEBUG) { + Slogf.d(TAG, "createBiInteractiveApp(biIAppUri=%s,params=%s)", biIAppUri, params); + } + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "createBiInteractiveApp"); + SessionState sessionState = null; + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).createBiInteractiveApp( + biIAppUri, params); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in createBiInteractiveApp", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void destroyBiInteractiveApp(IBinder sessionToken, String biIAppId, int userId) { + if (DEBUG) { + Slogf.d(TAG, "destroyBiInteractiveApp(biIAppId=%s)", biIAppId); + } + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "destroyBiInteractiveApp"); + SessionState sessionState = null; + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).destroyBiInteractiveApp(biIAppId); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in destroyBiInteractiveApp", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void setSurface(IBinder sessionToken, Surface surface, int userId) { final int callingUid = Binder.getCallingUid(); @@ -1164,7 +1220,8 @@ public class TvIAppManagerService extends SystemService { serviceState.mReconnecting = false; } - boolean shouldBind = !serviceState.mSessionTokens.isEmpty(); + boolean shouldBind = (!serviceState.mSessionTokens.isEmpty()) + || (serviceState.mPendingPrepare) || (!serviceState.mPendingAppLinkInfo.isEmpty()); if (serviceState.mService == null && shouldBind) { // This means that the service is not yet connected but its state indicates that we @@ -1584,6 +1641,25 @@ public class TvIAppManagerService extends SystemService { } } + @Override + public void onBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) { + synchronized (mLock) { + if (DEBUG) { + Slogf.d(TAG, "onBiInteractiveAppCreated (biIAppUri=" + biIAppUri + + ", biIAppId=" + biIAppId + ")"); + } + if (mSessionState.mSession == null || mSessionState.mClient == null) { + return; + } + try { + mSessionState.mClient.onBiInteractiveAppCreated( + biIAppUri, biIAppId, mSessionState.mSeq); + } catch (RemoteException e) { + Slogf.e(TAG, "error in onBiInteractiveAppCreated", e); + } + } + } + @GuardedBy("mLock") private boolean addSessionTokenToClientStateLocked(ITvIAppSession session) { try {