From 8a1c617b067b4a96ae853d9ad7474fd1bc174f10 Mon Sep 17 00:00:00 2001 From: shubang Date: Thu, 2 Dec 2021 16:40:15 -0800 Subject: [PATCH] TIAF: add state change methods Bug: 208651882 Test: mmm Change-Id: Icede0a9279f69266c14475369211239a841f31c5 --- .../media/tv/interactive/ITvIAppClient.aidl | 1 + .../interactive/ITvIAppManagerCallback.aidl | 1 + .../interactive/ITvIAppServiceCallback.aidl | 1 + .../interactive/ITvIAppSessionCallback.aidl | 1 + .../media/tv/interactive/TvIAppManager.java | 91 +++++++++++++++++- .../media/tv/interactive/TvIAppService.java | 55 ++++++++++- .../tv/interactive/TvIAppManagerService.java | 96 ++++++++++++++++++- 7 files changed, 241 insertions(+), 5 deletions(-) diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl index 9fc1fe71c3183..30ef50342c11c 100644 --- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl @@ -30,4 +30,5 @@ oneway interface ITvIAppClient { void onSessionReleased(int seq); 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); } \ No newline at end of file diff --git a/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl index 77a09b72bc2e2..d5e0c639acc30 100644 --- a/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppManagerCallback.aidl @@ -27,4 +27,5 @@ interface ITvIAppManagerCallback { void onIAppServiceRemoved(in String iAppServiceId); void onIAppServiceUpdated(in String iAppServiceId); void onTvIAppInfoUpdated(in TvIAppInfo tvIAppInfo); + void onStateChanged(in String iAppServiceId, int type, int state); } \ No newline at end of file diff --git a/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl index 8d49bc22c7382..fec7d7804f305 100644 --- a/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppServiceCallback.aidl @@ -22,4 +22,5 @@ package android.media.tv.interactive; * @hide */ oneway interface ITvIAppServiceCallback { + void onStateChanged(int type, int state); } \ No newline at end of file diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl index d2b966ed939fd..ff8af88fa5fa4 100644 --- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl @@ -29,4 +29,5 @@ oneway interface ITvIAppSessionCallback { void onSessionCreated(in ITvIAppSession session); void onLayoutSurface(int left, int top, int right, int bottom); void onBroadcastInfoRequest(in BroadcastInfoRequest request); + void onSessionStateChanged(int state); } \ No newline at end of file diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java index fed976980cd76..2272084a4017c 100644 --- a/media/java/android/media/tv/interactive/TvIAppManager.java +++ b/media/java/android/media/tv/interactive/TvIAppManager.java @@ -16,6 +16,7 @@ package android.media.tv.interactive; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; @@ -41,6 +42,8 @@ import android.view.View; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -54,6 +57,36 @@ public final class TvIAppManager { // TODO: cleanup and unhide public APIs private static final String TAG = "TvIAppManager"; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = false, prefix = "TV_IAPP_RTE_STATE_", value = { + TV_IAPP_RTE_STATE_UNREALIZED, + TV_IAPP_RTE_STATE_PREPARING, + TV_IAPP_RTE_STATE_READY, + TV_IAPP_RTE_STATE_ERROR}) + public @interface TvIAppRteState {} + + /** + * Unrealized state of interactive app RTE. + * @hide + */ + public static final int TV_IAPP_RTE_STATE_UNREALIZED = 1; + /** + * Preparing state of interactive app RTE. + * @hide + */ + public static final int TV_IAPP_RTE_STATE_PREPARING = 2; + /** + * Ready state of interactive app RTE. + * @hide + */ + public static final int TV_IAPP_RTE_STATE_READY = 3; + /** + * Error state of interactive app RTE. + * @hide + */ + public static final int TV_IAPP_RTE_STATE_ERROR = 4; + private final ITvIAppManager mService; private final int mUserId; @@ -134,9 +167,20 @@ public final class TvIAppManager { record.postBroadcastInfoRequest(request); } } + + @Override + public void onSessionStateChanged(int state, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postSessionStateChanged(state); + } + } }; ITvIAppManagerCallback managerCallback = new ITvIAppManagerCallback.Stub() { - // TODO: handle IApp service state changes @Override public void onIAppServiceAdded(String iAppServiceId) { synchronized (mLock) { @@ -173,6 +217,15 @@ public final class TvIAppManager { } } } + + @Override + public void onStateChanged(String iAppServiceId, int type, int state) { + synchronized (mLock) { + for (TvIAppCallbackRecord record : mCallbackRecords) { + record.postStateChanged(iAppServiceId, type, state); + } + } + } }; try { if (mService != null) { @@ -233,6 +286,15 @@ public final class TvIAppManager { */ public void onTvIAppInfoUpdated(TvIAppInfo iAppInfo) { } + + + /** + * This is called when the state of the interactive app service is changed. + * @hide + */ + public void onTvIAppServiceStateChanged( + String iAppServiceId, int type, @TvIAppRteState int state) { + } } private static final class TvIAppCallbackRecord { @@ -283,6 +345,15 @@ public final class TvIAppManager { } }); } + + public void postStateChanged(String iAppServiceId, int type, int state) { + mHandler.post(new Runnable() { + @Override + public void run() { + mCallback.onTvIAppServiceStateChanged(iAppServiceId, type, state); + } + }); + } } /** @@ -875,6 +946,15 @@ public final class TvIAppManager { } }); } + + void postSessionStateChanged(int state) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onSessionStateChanged(mSession, state); + } + }); + } } /** @@ -912,5 +992,14 @@ public final class TvIAppManager { */ public void onLayoutSurface(Session session, int left, int top, int right, int bottom) { } + + /** + * This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called. + * + * @param session A {@link TvIAppManager.Session} associated with this callback. + * @param state the current state. + */ + public void onSessionStateChanged(Session session, int state) { + } } } diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java index 027b8909fe63c..f93b59784c7ef 100644 --- a/media/java/android/media/tv/interactive/TvIAppService.java +++ b/media/java/android/media/tv/interactive/TvIAppService.java @@ -152,6 +152,16 @@ public abstract class TvIAppService extends Service { return null; } + /** + * Notifies the system when the state of the interactive app has been changed. + * @param state the current state + * @hide + */ + public final void notifyStateChanged(int type, @TvIAppManager.TvIAppRteState int state) { + mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED, + type, state).sendToTarget(); + } + /** * Base class for derived classes to implement to provide a TV interactive app session. * @hide @@ -384,7 +394,7 @@ public abstract class TvIAppService extends Service { /** * Requests broadcast related information from the related TV input. - * @param request + * @param request the request for broadcast info */ public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) { executeOrPostRunnableOnMainThread(new Runnable() { @@ -444,6 +454,30 @@ public abstract class TvIAppService extends Service { onBroadcastInfoResponse(response); } + /** + * Notifies when the session state is changed. + * @param state the current state. + */ + public void notifySessionStateChanged(@TvIAppManager.TvIAppRteState int state) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "notifySessionStateChanged (state=" + + state + ")"); + } + if (mSessionCallback != null) { + mSessionCallback.onSessionStateChanged(state); + } + } catch (RemoteException e) { + Log.w(TAG, "error in notifySessionStateChanged", e); + } + } + }); + } + /** * Takes care of dispatching incoming input events and tells whether the event was handled. */ @@ -747,6 +781,19 @@ public abstract class TvIAppService extends Service { private final class ServiceHandler extends Handler { private static final int DO_CREATE_SESSION = 1; private static final int DO_NOTIFY_SESSION_CREATED = 2; + private static final int DO_NOTIFY_RTE_STATE_CHANGED = 3; + + private void broadcastRteStateChanged(int type, int state) { + int n = mCallbacks.beginBroadcast(); + for (int i = 0; i < n; ++i) { + try { + mCallbacks.getBroadcastItem(i).onStateChanged(type, state); + } catch (RemoteException e) { + Log.e(TAG, "error in broadcastRteStateChanged", e); + } + } + mCallbacks.finishBroadcast(); + } @Override public void handleMessage(Message msg) { @@ -795,6 +842,12 @@ public abstract class TvIAppService extends Service { args.recycle(); return; } + case DO_NOTIFY_RTE_STATE_CHANGED: { + int type = msg.arg1; + int state = msg.arg2; + broadcastRteStateChanged(type, state); + return; + } default: { Log.w(TAG, "Unhandled message code: " + msg.what); return; 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 f12139d07f11c..122b3f35d8d1c 100644 --- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java @@ -239,6 +239,24 @@ public class TvIAppManagerService extends SystemService { userState.mCallbacks.finishBroadcast(); } + @GuardedBy("mLock") + private void notifyStateChangedLocked( + UserState userState, String iAppServiceId, int type, int state) { + if (DEBUG) { + Slog.d(TAG, "notifyRteStateChanged(iAppServiceId=" + + iAppServiceId + ", type=" + type + ", state=" + state + ")"); + } + int n = userState.mCallbacks.beginBroadcast(); + for (int i = 0; i < n; ++i) { + try { + userState.mCallbacks.getBroadcastItem(i).onStateChanged(iAppServiceId, type, state); + } catch (RemoteException e) { + Slog.e(TAG, "failed to report RTE state changed", e); + } + } + userState.mCallbacks.finishBroadcast(); + } + private int getIAppUid(TvIAppInfo info) { try { return getContext().getPackageManager().getApplicationInfo( @@ -545,6 +563,17 @@ public class TvIAppManagerService extends SystemService { return mUserStates.get(userId); } + @GuardedBy("mLock") + private ServiceState getServiceStateLocked(ComponentName component, int userId) { + UserState userState = getOrCreateUserStateLocked(userId); + ServiceState serviceState = userState.mServiceStateMap.get(component); + if (serviceState == null) { + throw new IllegalStateException("Service state not found for " + component + " (userId=" + + userId + ")"); + } + return serviceState; + } + @GuardedBy("mLock") private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) { UserState userState = getOrCreateUserStateLocked(userId); @@ -617,7 +646,11 @@ public class TvIAppManagerService extends SystemService { } ComponentName componentName = iAppState.mInfo.getComponent(); ServiceState serviceState = userState.mServiceStateMap.get(componentName); - if (serviceState != null) { + if (serviceState == null) { + serviceState = new ServiceState( + componentName, tiasId, resolvedUserId, true, type); + userState.mServiceStateMap.put(componentName, serviceState); + } else if (serviceState.mService != null) { serviceState.mService.prepare(type); } } @@ -657,7 +690,8 @@ public class TvIAppManagerService extends SystemService { if (serviceState == null) { int tiasUid = PackageManager.getApplicationInfoAsUserCached( iAppState.mComponentName.getPackageName(), 0, resolvedUserId).uid; - serviceState = new ServiceState(iAppState.mComponentName, resolvedUserId); + serviceState = new ServiceState( + iAppState.mComponentName, iAppServiceId, resolvedUserId); userState.mServiceStateMap.put(iAppState.mComponentName, serviceState); } // Send a null token immediately while reconnecting. @@ -1202,15 +1236,26 @@ public class TvIAppManagerService extends SystemService { private final List mSessionTokens = new ArrayList<>(); private final ServiceConnection mConnection; private final ComponentName mComponent; + private final String mIAppSeriviceId; + private boolean mPendingPrepare = false; + private Integer mPendingPrepareType = null; private ITvIAppService mService; private ServiceCallback mCallback; private boolean mBound; private boolean mReconnecting; - private ServiceState(ComponentName component, int userId) { + private ServiceState(ComponentName component, String tias, int userId) { + this(component, tias, userId, false, null); + } + + private ServiceState(ComponentName component, String tias, int userId, + boolean pendingPrepare, Integer prepareType) { mComponent = component; + mPendingPrepare = pendingPrepare; + mPendingPrepareType = prepareType; mConnection = new IAppServiceConnection(component, userId); + mIAppSeriviceId = tias; } } @@ -1238,6 +1283,19 @@ public class TvIAppManagerService extends SystemService { ServiceState serviceState = userState.mServiceStateMap.get(mComponent); serviceState.mService = ITvIAppService.Stub.asInterface(service); + if (serviceState.mPendingPrepare) { + final long identity = Binder.clearCallingIdentity(); + try { + serviceState.mService.prepare(serviceState.mPendingPrepareType); + serviceState.mPendingPrepare = false; + serviceState.mPendingPrepareType = null; + } catch (RemoteException e) { + Slogf.e(TAG, "error in prepare when onServiceConnected", e); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + List tokensToBeRemoved = new ArrayList<>(); // And create sessions, if any. @@ -1286,6 +1344,21 @@ public class TvIAppManagerService extends SystemService { mComponent = component; mUserId = userId; } + + @Override + public void onStateChanged(int type, int state) { + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); + String iAppServiceId = serviceState.mIAppSeriviceId; + UserState userState = getUserStateLocked(mUserId); + notifyStateChangedLocked(userState, iAppServiceId, type, state); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } } private final class SessionCallback extends ITvIAppSessionCallback.Stub { @@ -1358,6 +1431,23 @@ public class TvIAppManagerService extends SystemService { } } + @Override + public void onSessionStateChanged(int state) { + synchronized (mLock) { + if (DEBUG) { + Slogf.d(TAG, "onSessionStateChanged (state=" + state + ")"); + } + if (mSessionState.mSession == null || mSessionState.mClient == null) { + return; + } + try { + mSessionState.mClient.onSessionStateChanged(state, mSessionState.mSeq); + } catch (RemoteException e) { + Slogf.e(TAG, "error in onSessionStateChanged", e); + } + } + } + @GuardedBy("mLock") private boolean addSessionTokenToClientStateLocked(ITvIAppSession session) { try {