Loading media/java/android/media/tv/interactive/ITvIAppService.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.media.tv.interactive; import android.media.tv.interactive.ITvIAppServiceCallback; import android.media.tv.interactive.ITvIAppSessionCallback; /** Loading @@ -24,5 +25,7 @@ import android.media.tv.interactive.ITvIAppSessionCallback; * @hide */ oneway interface ITvIAppService { void registerCallback(in ITvIAppServiceCallback callback); void unregisterCallback(in ITvIAppServiceCallback callback); void createSession(in ITvIAppSessionCallback callback, in String iAppServiceId, int type); } No newline at end of file media/java/android/media/tv/interactive/TvIAppService.java +17 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; import android.view.KeyEvent; Loading Loading @@ -62,11 +63,26 @@ public abstract class TvIAppService extends Service { public static final String SERVICE_META_DATA = "android.media.tv.interactive.app"; private final Handler mServiceHandler = new ServiceHandler(); private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks = new RemoteCallbackList<>(); /** @hide */ @Override public final IBinder onBind(Intent intent) { ITvIAppService.Stub tvIAppServiceBinder = new ITvIAppService.Stub() { @Override public void registerCallback(ITvIAppServiceCallback cb) { if (cb != null) { mCallbacks.register(cb); } } @Override public void unregisterCallback(ITvIAppServiceCallback cb) { if (cb != null) { mCallbacks.unregister(cb); } } @Override public void createSession(ITvIAppSessionCallback cb, String iAppServiceId, int type) { Loading Loading @@ -137,7 +153,7 @@ public abstract class TvIAppService extends Service { * Called when the application sets the surface. * * <p>The TV IApp service should render interactive app UI onto the given surface. When * called with {@code null}, the input service should immediately free any references to the * called with {@code null}, the IApp service should immediately free any references to the * currently set surface and stop using it. * * @param surface The surface to be used for interactive app UI rendering. Can be Loading services/core/java/com/android/server/tv/TvInputManagerService.java +9 −7 Original line number Diff line number Diff line Loading @@ -470,6 +470,7 @@ public final class TvInputManagerService extends SystemService { } private void stopUser(int userId) { synchronized (mLock) { if (userId == mCurrentUserId) { switchUser(ActivityManager.getCurrentUser()); return; Loading @@ -479,6 +480,7 @@ public final class TvInputManagerService extends SystemService { unbindServiceOfUserLocked(userId); mRunningProfiles.remove(userId); } } private void startProfileLocked(int userId) { mRunningProfiles.add(userId); Loading services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java +185 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.media.tv.interactive.ITvIAppClient; import android.media.tv.interactive.ITvIAppManager; import android.media.tv.interactive.ITvIAppManagerCallback; Loading @@ -42,6 +43,7 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; import android.util.Slog; import android.util.SparseArray; Loading @@ -58,10 +60,12 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * This class provides a system service that manages interactive TV applications. */ Loading @@ -81,6 +85,8 @@ public class TvIAppManagerService extends SystemService { @GuardedBy("mLock") private final SparseArray<UserState> mUserStates = new SparseArray<>(); private final UserManager mUserManager; /** * Initializes the system service. * <p> Loading @@ -93,6 +99,7 @@ public class TvIAppManagerService extends SystemService { public TvIAppManagerService(Context context) { super(context); mContext = context; mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); } @GuardedBy("mLock") Loading Loading @@ -330,11 +337,188 @@ public class TvIAppManagerService extends SystemService { mContext.registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO: handle switch / start / stop user String action = intent.getAction(); if (Intent.ACTION_USER_SWITCHED.equals(action)) { switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); } else if (Intent.ACTION_USER_STARTED.equals(action)) { int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); startUser(userId); } else if (Intent.ACTION_USER_STOPPED.equals(action)) { int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); stopUser(userId); } } }, UserHandle.ALL, intentFilter, null, null); } private void switchUser(int userId) { synchronized (mLock) { if (mCurrentUserId == userId) { return; } UserInfo userInfo = mUserManager.getUserInfo(userId); if (userInfo.isProfile()) { Slog.w(TAG, "cannot switch to a profile!"); return; } for (int runningId : mRunningProfiles) { releaseSessionOfUserLocked(runningId); unbindServiceOfUserLocked(runningId); } mRunningProfiles.clear(); releaseSessionOfUserLocked(mCurrentUserId); unbindServiceOfUserLocked(mCurrentUserId); mCurrentUserId = userId; buildTvIAppServiceListLocked(userId, null); } } private void removeUser(int userId) { synchronized (mLock) { UserState userState = getUserStateLocked(userId); if (userState == null) { return; } // Release all created sessions. for (SessionState state : userState.mSessionStateMap.values()) { if (state.mSession != null) { try { state.mSession.release(); } catch (RemoteException e) { Slog.e(TAG, "error in release", e); } } } userState.mSessionStateMap.clear(); // Unregister all callbacks and unbind all services. for (ServiceState serviceState : userState.mServiceStateMap.values()) { if (serviceState.mService != null) { if (serviceState.mCallback != null) { try { serviceState.mService.unregisterCallback(serviceState.mCallback); } catch (RemoteException e) { Slog.e(TAG, "error in unregisterCallback", e); } } mContext.unbindService(serviceState.mConnection); } } userState.mServiceStateMap.clear(); // Clear everything else. userState.mIAppMap.clear(); userState.mPackageSet.clear(); userState.mClientStateMap.clear(); userState.mCallbacks.kill(); mRunningProfiles.remove(userId); mUserStates.remove(userId); if (userId == mCurrentUserId) { switchUser(UserHandle.USER_SYSTEM); } } } private void startUser(int userId) { synchronized (mLock) { if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) { // user already started return; } UserInfo userInfo = mUserManager.getUserInfo(userId); UserInfo parentInfo = mUserManager.getProfileParent(userId); if (userInfo.isProfile() && parentInfo != null && parentInfo.id == mCurrentUserId) { // only the children of the current user can be started in background startProfileLocked(userId); } } } private void stopUser(int userId) { synchronized (mLock) { if (userId == mCurrentUserId) { switchUser(ActivityManager.getCurrentUser()); return; } releaseSessionOfUserLocked(userId); unbindServiceOfUserLocked(userId); mRunningProfiles.remove(userId); } } @GuardedBy("mLock") private void startProfileLocked(int userId) { mRunningProfiles.add(userId); buildTvIAppServiceListLocked(userId, null); } @GuardedBy("mLock") private void releaseSessionOfUserLocked(int userId) { UserState userState = getUserStateLocked(userId); if (userState == null) { return; } List<SessionState> sessionStatesToRelease = new ArrayList<>(); for (SessionState sessionState : userState.mSessionStateMap.values()) { if (sessionState.mSession != null) { sessionStatesToRelease.add(sessionState); } } for (SessionState sessionState : sessionStatesToRelease) { try { sessionState.mSession.release(); } catch (RemoteException e) { Slog.e(TAG, "error in release", e); } clearSessionAndNotifyClientLocked(sessionState); } } @GuardedBy("mLock") private void unbindServiceOfUserLocked(int userId) { UserState userState = getUserStateLocked(userId); if (userState == null) { return; } for (Iterator<ComponentName> it = userState.mServiceStateMap.keySet().iterator(); it.hasNext(); ) { ComponentName component = it.next(); ServiceState serviceState = userState.mServiceStateMap.get(component); if (serviceState != null && serviceState.mSessionTokens.isEmpty()) { if (serviceState.mCallback != null) { try { serviceState.mService.unregisterCallback(serviceState.mCallback); } catch (RemoteException e) { Slog.e(TAG, "error in unregisterCallback", e); } } mContext.unbindService(serviceState.mConnection); it.remove(); } } } @GuardedBy("mLock") private void clearSessionAndNotifyClientLocked(SessionState state) { if (state.mClient != null) { try { state.mClient.onSessionReleased(state.mSeq); } catch (RemoteException e) { Slog.e(TAG, "error in onSessionReleased", e); } } removeSessionStateLocked(state.mSessionToken, state.mUserId); } private SessionState getSessionState(IBinder sessionToken) { // TODO: implement user state and get session from it. return null; Loading Loading
media/java/android/media/tv/interactive/ITvIAppService.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.media.tv.interactive; import android.media.tv.interactive.ITvIAppServiceCallback; import android.media.tv.interactive.ITvIAppSessionCallback; /** Loading @@ -24,5 +25,7 @@ import android.media.tv.interactive.ITvIAppSessionCallback; * @hide */ oneway interface ITvIAppService { void registerCallback(in ITvIAppServiceCallback callback); void unregisterCallback(in ITvIAppServiceCallback callback); void createSession(in ITvIAppSessionCallback callback, in String iAppServiceId, int type); } No newline at end of file
media/java/android/media/tv/interactive/TvIAppService.java +17 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; import android.view.KeyEvent; Loading Loading @@ -62,11 +63,26 @@ public abstract class TvIAppService extends Service { public static final String SERVICE_META_DATA = "android.media.tv.interactive.app"; private final Handler mServiceHandler = new ServiceHandler(); private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks = new RemoteCallbackList<>(); /** @hide */ @Override public final IBinder onBind(Intent intent) { ITvIAppService.Stub tvIAppServiceBinder = new ITvIAppService.Stub() { @Override public void registerCallback(ITvIAppServiceCallback cb) { if (cb != null) { mCallbacks.register(cb); } } @Override public void unregisterCallback(ITvIAppServiceCallback cb) { if (cb != null) { mCallbacks.unregister(cb); } } @Override public void createSession(ITvIAppSessionCallback cb, String iAppServiceId, int type) { Loading Loading @@ -137,7 +153,7 @@ public abstract class TvIAppService extends Service { * Called when the application sets the surface. * * <p>The TV IApp service should render interactive app UI onto the given surface. When * called with {@code null}, the input service should immediately free any references to the * called with {@code null}, the IApp service should immediately free any references to the * currently set surface and stop using it. * * @param surface The surface to be used for interactive app UI rendering. Can be Loading
services/core/java/com/android/server/tv/TvInputManagerService.java +9 −7 Original line number Diff line number Diff line Loading @@ -470,6 +470,7 @@ public final class TvInputManagerService extends SystemService { } private void stopUser(int userId) { synchronized (mLock) { if (userId == mCurrentUserId) { switchUser(ActivityManager.getCurrentUser()); return; Loading @@ -479,6 +480,7 @@ public final class TvInputManagerService extends SystemService { unbindServiceOfUserLocked(userId); mRunningProfiles.remove(userId); } } private void startProfileLocked(int userId) { mRunningProfiles.add(userId); Loading
services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java +185 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.media.tv.interactive.ITvIAppClient; import android.media.tv.interactive.ITvIAppManager; import android.media.tv.interactive.ITvIAppManagerCallback; Loading @@ -42,6 +43,7 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; import android.util.Slog; import android.util.SparseArray; Loading @@ -58,10 +60,12 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * This class provides a system service that manages interactive TV applications. */ Loading @@ -81,6 +85,8 @@ public class TvIAppManagerService extends SystemService { @GuardedBy("mLock") private final SparseArray<UserState> mUserStates = new SparseArray<>(); private final UserManager mUserManager; /** * Initializes the system service. * <p> Loading @@ -93,6 +99,7 @@ public class TvIAppManagerService extends SystemService { public TvIAppManagerService(Context context) { super(context); mContext = context; mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); } @GuardedBy("mLock") Loading Loading @@ -330,11 +337,188 @@ public class TvIAppManagerService extends SystemService { mContext.registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO: handle switch / start / stop user String action = intent.getAction(); if (Intent.ACTION_USER_SWITCHED.equals(action)) { switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); } else if (Intent.ACTION_USER_STARTED.equals(action)) { int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); startUser(userId); } else if (Intent.ACTION_USER_STOPPED.equals(action)) { int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); stopUser(userId); } } }, UserHandle.ALL, intentFilter, null, null); } private void switchUser(int userId) { synchronized (mLock) { if (mCurrentUserId == userId) { return; } UserInfo userInfo = mUserManager.getUserInfo(userId); if (userInfo.isProfile()) { Slog.w(TAG, "cannot switch to a profile!"); return; } for (int runningId : mRunningProfiles) { releaseSessionOfUserLocked(runningId); unbindServiceOfUserLocked(runningId); } mRunningProfiles.clear(); releaseSessionOfUserLocked(mCurrentUserId); unbindServiceOfUserLocked(mCurrentUserId); mCurrentUserId = userId; buildTvIAppServiceListLocked(userId, null); } } private void removeUser(int userId) { synchronized (mLock) { UserState userState = getUserStateLocked(userId); if (userState == null) { return; } // Release all created sessions. for (SessionState state : userState.mSessionStateMap.values()) { if (state.mSession != null) { try { state.mSession.release(); } catch (RemoteException e) { Slog.e(TAG, "error in release", e); } } } userState.mSessionStateMap.clear(); // Unregister all callbacks and unbind all services. for (ServiceState serviceState : userState.mServiceStateMap.values()) { if (serviceState.mService != null) { if (serviceState.mCallback != null) { try { serviceState.mService.unregisterCallback(serviceState.mCallback); } catch (RemoteException e) { Slog.e(TAG, "error in unregisterCallback", e); } } mContext.unbindService(serviceState.mConnection); } } userState.mServiceStateMap.clear(); // Clear everything else. userState.mIAppMap.clear(); userState.mPackageSet.clear(); userState.mClientStateMap.clear(); userState.mCallbacks.kill(); mRunningProfiles.remove(userId); mUserStates.remove(userId); if (userId == mCurrentUserId) { switchUser(UserHandle.USER_SYSTEM); } } } private void startUser(int userId) { synchronized (mLock) { if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) { // user already started return; } UserInfo userInfo = mUserManager.getUserInfo(userId); UserInfo parentInfo = mUserManager.getProfileParent(userId); if (userInfo.isProfile() && parentInfo != null && parentInfo.id == mCurrentUserId) { // only the children of the current user can be started in background startProfileLocked(userId); } } } private void stopUser(int userId) { synchronized (mLock) { if (userId == mCurrentUserId) { switchUser(ActivityManager.getCurrentUser()); return; } releaseSessionOfUserLocked(userId); unbindServiceOfUserLocked(userId); mRunningProfiles.remove(userId); } } @GuardedBy("mLock") private void startProfileLocked(int userId) { mRunningProfiles.add(userId); buildTvIAppServiceListLocked(userId, null); } @GuardedBy("mLock") private void releaseSessionOfUserLocked(int userId) { UserState userState = getUserStateLocked(userId); if (userState == null) { return; } List<SessionState> sessionStatesToRelease = new ArrayList<>(); for (SessionState sessionState : userState.mSessionStateMap.values()) { if (sessionState.mSession != null) { sessionStatesToRelease.add(sessionState); } } for (SessionState sessionState : sessionStatesToRelease) { try { sessionState.mSession.release(); } catch (RemoteException e) { Slog.e(TAG, "error in release", e); } clearSessionAndNotifyClientLocked(sessionState); } } @GuardedBy("mLock") private void unbindServiceOfUserLocked(int userId) { UserState userState = getUserStateLocked(userId); if (userState == null) { return; } for (Iterator<ComponentName> it = userState.mServiceStateMap.keySet().iterator(); it.hasNext(); ) { ComponentName component = it.next(); ServiceState serviceState = userState.mServiceStateMap.get(component); if (serviceState != null && serviceState.mSessionTokens.isEmpty()) { if (serviceState.mCallback != null) { try { serviceState.mService.unregisterCallback(serviceState.mCallback); } catch (RemoteException e) { Slog.e(TAG, "error in unregisterCallback", e); } } mContext.unbindService(serviceState.mConnection); it.remove(); } } } @GuardedBy("mLock") private void clearSessionAndNotifyClientLocked(SessionState state) { if (state.mClient != null) { try { state.mClient.onSessionReleased(state.mSeq); } catch (RemoteException e) { Slog.e(TAG, "error in onSessionReleased", e); } } removeSessionStateLocked(state.mSessionToken, state.mUserId); } private SessionState getSessionState(IBinder sessionToken) { // TODO: implement user state and get session from it. return null; Loading