summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt10
-rw-r--r--core/java/android/tv/ITvInputClient.aidl1
-rw-r--r--core/java/android/tv/TvInputManager.java111
-rw-r--r--core/java/android/tv/TvView.java30
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java162
5 files changed, 230 insertions, 84 deletions
diff --git a/api/current.txt b/api/current.txt
index 8513b5fca7a5..17f5e5394cd7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29179,7 +29179,7 @@ package android.tv {
}
public final class TvInputManager {
- method public void createSession(java.lang.String, android.tv.TvInputManager.SessionCreateCallback, android.os.Handler);
+ method public void createSession(java.lang.String, android.tv.TvInputManager.SessionCallback, android.os.Handler);
method public boolean getAvailability(java.lang.String);
method public java.util.List<android.tv.TvInputInfo> getTvInputList();
method public void registerListener(java.lang.String, android.tv.TvInputManager.TvInputListener, android.os.Handler);
@@ -29192,8 +29192,10 @@ package android.tv {
method public void tune(android.net.Uri);
}
- public static abstract interface TvInputManager.SessionCreateCallback {
- method public abstract void onSessionCreated(android.tv.TvInputManager.Session);
+ public static abstract class TvInputManager.SessionCallback {
+ ctor public TvInputManager.SessionCallback();
+ method public void onSessionCreated(android.tv.TvInputManager.Session);
+ method public void onSessionReleased(android.tv.TvInputManager.Session);
}
public static abstract class TvInputManager.TvInputListener {
@@ -29230,7 +29232,7 @@ package android.tv {
ctor public TvView(android.content.Context);
ctor public TvView(android.content.Context, android.util.AttributeSet);
ctor public TvView(android.content.Context, android.util.AttributeSet, int);
- method public void bindTvInput(java.lang.String, android.tv.TvInputManager.SessionCreateCallback);
+ method public void bindTvInput(java.lang.String, android.tv.TvInputManager.SessionCallback);
method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
method public void setOnUnhandledInputEventListener(android.tv.TvView.OnUnhandledInputEventListener);
diff --git a/core/java/android/tv/ITvInputClient.aidl b/core/java/android/tv/ITvInputClient.aidl
index 2adcaf108308..ac83356a969e 100644
--- a/core/java/android/tv/ITvInputClient.aidl
+++ b/core/java/android/tv/ITvInputClient.aidl
@@ -28,4 +28,5 @@ import android.view.InputChannel;
oneway interface ITvInputClient {
void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
void onAvailabilityChanged(in String inputId, boolean isAvailable);
+ void onSessionReleased(int seq);
}
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
index e246fcf8dd1a..c5f179a337c0 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/core/java/android/tv/TvInputManager.java
@@ -52,12 +52,12 @@ public final class TvInputManager {
private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
new HashMap<String, List<TvInputListenerRecord>>();
- // A mapping from the sequence number of a session to its SessionCreateCallbackRecord.
- private final SparseArray<SessionCreateCallbackRecord> mSessionCreateCallbackRecordMap =
- new SparseArray<SessionCreateCallbackRecord>();
+ // A mapping from the sequence number of a session to its SessionCallbackRecord.
+ private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
+ new SparseArray<SessionCallbackRecord>();
// A sequence number for the next session to be created. Should be protected by a lock
- // {@code mSessionCreateCallbackRecordMap}.
+ // {@code mSessionCallbackRecordMap}.
private int mNextSeq;
private final ITvInputClient mClient;
@@ -67,31 +67,52 @@ public final class TvInputManager {
/**
* Interface used to receive the created session.
*/
- public interface SessionCreateCallback {
+ public abstract static class SessionCallback {
/**
* This is called after {@link TvInputManager#createSession} has been processed.
*
* @param session A {@link TvInputManager.Session} instance created. This can be
* {@code null} if the creation request failed.
*/
- void onSessionCreated(Session session);
+ public void onSessionCreated(Session session) {
+ }
+
+ /**
+ * This is called when {@link TvInputManager.Session} is released.
+ * This typically happens when the process hosting the session has crashed or been killed.
+ *
+ * @param session A {@link TvInputManager.Session} instance released.
+ */
+ public void onSessionReleased(Session session) {
+ }
}
- private static final class SessionCreateCallbackRecord {
- private final SessionCreateCallback mSessionCreateCallback;
+ private static final class SessionCallbackRecord {
+ private final SessionCallback mSessionCallback;
private final Handler mHandler;
+ private Session mSession;
- public SessionCreateCallbackRecord(SessionCreateCallback sessionCreateCallback,
+ public SessionCallbackRecord(SessionCallback sessionCallback,
Handler handler) {
- mSessionCreateCallback = sessionCreateCallback;
+ mSessionCallback = sessionCallback;
mHandler = handler;
}
public void postSessionCreated(final Session session) {
+ mSession = session;
mHandler.post(new Runnable() {
@Override
public void run() {
- mSessionCreateCallback.onSessionCreated(session);
+ mSessionCallback.onSessionCreated(session);
+ }
+ });
+ }
+
+ public void postSessionReleased() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onSessionReleased(mSession);
}
});
}
@@ -145,22 +166,36 @@ public final class TvInputManager {
@Override
public void onSessionCreated(String inputId, IBinder token, InputChannel channel,
int seq) {
- synchronized (mSessionCreateCallbackRecordMap) {
- SessionCreateCallbackRecord record = mSessionCreateCallbackRecordMap.get(seq);
- mSessionCreateCallbackRecordMap.delete(seq);
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
Log.e(TAG, "Callback not found for " + token);
return;
}
Session session = null;
if (token != null) {
- session = new Session(token, channel, mService, mUserId);
+ session = new Session(token, channel, mService, mUserId, seq,
+ mSessionCallbackRecordMap);
}
record.postSessionCreated(session);
}
}
@Override
+ public void onSessionReleased(int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ mSessionCallbackRecordMap.delete(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq:" + seq);
+ return;
+ }
+ record.mSession.releaseInternal();
+ record.postSessionReleased();
+ }
+ }
+
+ @Override
public void onAvailabilityChanged(String inputId, boolean isAvailable) {
synchronized (mTvInputListenerRecordsMap) {
List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
@@ -298,7 +333,7 @@ public final class TvInputManager {
* @param handler a {@link Handler} that the session creation will be delivered to.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
*/
- public void createSession(String inputId, final SessionCreateCallback callback,
+ public void createSession(String inputId, final SessionCallback callback,
Handler handler) {
if (inputId == null) {
throw new IllegalArgumentException("id cannot be null");
@@ -309,10 +344,10 @@ public final class TvInputManager {
if (handler == null) {
throw new IllegalArgumentException("handler cannot be null");
}
- SessionCreateCallbackRecord record = new SessionCreateCallbackRecord(callback, handler);
- synchronized (mSessionCreateCallbackRecordMap) {
+ SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
+ synchronized (mSessionCallbackRecordMap) {
int seq = mNextSeq++;
- mSessionCreateCallbackRecordMap.put(seq, record);
+ mSessionCallbackRecordMap.put(seq, record);
try {
mService.createSession(mClient, inputId, seq, mUserId);
} catch (RemoteException e) {
@@ -331,6 +366,7 @@ public final class TvInputManager {
private final ITvInputManager mService;
private final int mUserId;
+ private final int mSeq;
// For scheduling input event handling on the main thread. This also serves as a lock to
// protect pending input events and the input channel.
@@ -338,17 +374,21 @@ public final class TvInputManager {
private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
+ private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
private IBinder mToken;
private TvInputEventSender mSender;
private InputChannel mChannel;
/** @hide */
- private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId) {
+ private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
+ int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
mToken = token;
mChannel = channel;
mService = service;
mUserId = userId;
+ mSeq = seq;
+ mSessionCallbackRecordMap = sessionCallbackRecordMap;
}
/**
@@ -362,22 +402,11 @@ public final class TvInputManager {
}
try {
mService.releaseSession(mToken, mUserId);
- mToken = null;
} catch (RemoteException e) {
throw new RuntimeException(e);
}
- synchronized (mHandler) {
- if (mChannel != null) {
- if (mSender != null) {
- flushPendingEventsLocked();
- mSender.dispose();
- mSender = null;
- }
- mChannel.dispose();
- mChannel = null;
- }
- }
+ releaseInternal();
}
/**
@@ -669,6 +698,24 @@ public final class TvInputManager {
mPendingEventPool.release(p);
}
+ private void releaseInternal() {
+ mToken = null;
+ synchronized (mHandler) {
+ if (mChannel != null) {
+ if (mSender != null) {
+ flushPendingEventsLocked();
+ mSender.dispose();
+ mSender = null;
+ }
+ mChannel.dispose();
+ mChannel = null;
+ }
+ }
+ synchronized (mSessionCallbackRecordMap) {
+ mSessionCallbackRecordMap.remove(mSeq);
+ }
+ }
+
private final class InputEventHandler extends Handler {
public static final int MSG_SEND_INPUT_EVENT = 1;
public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
index e84659cc820f..80501e872072 100644
--- a/core/java/android/tv/TvView.java
+++ b/core/java/android/tv/TvView.java
@@ -22,7 +22,7 @@ import android.os.Handler;
import android.text.TextUtils;
import android.tv.TvInputManager.Session;
import android.tv.TvInputManager.Session.FinishedInputEventCallback;
-import android.tv.TvInputManager.SessionCreateCallback;
+import android.tv.TvInputManager.SessionCallback;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InputEvent;
@@ -46,7 +46,7 @@ public class TvView extends SurfaceView {
private boolean mOverlayViewCreated;
private Rect mOverlayViewFrame;
private final TvInputManager mTvInputManager;
- private SessionCreateCallback mSessionCreateCallback;
+ private SessionCallback mSessionCallback;
private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@@ -108,7 +108,7 @@ public class TvView extends SurfaceView {
}
/**
- * Binds a TV input to this view. {@link SessionCreateCallback#onSessionCreated} will be
+ * Binds a TV input to this view. {@link SessionCallback#onSessionCreated} will be
* called to send the result of this binding with {@link TvInputManager.Session}.
* If a TV input is already bound, the input will be unbound from this view and its session
* will be released.
@@ -118,7 +118,7 @@ public class TvView extends SurfaceView {
* {@link TvInputManager.Session}
* @throws IllegalArgumentException if any of the arguments is {@code null}.
*/
- public void bindTvInput(String inputId, SessionCreateCallback callback) {
+ public void bindTvInput(String inputId, SessionCallback callback) {
if (TextUtils.isEmpty(inputId)) {
throw new IllegalArgumentException("inputId cannot be null or an empty string");
}
@@ -130,11 +130,11 @@ public class TvView extends SurfaceView {
}
// When bindTvInput is called multiple times before the callback is called,
// only the callback of the last bindTvInput call will be actually called back.
- // The previous callbacks will be ignored. For the logic, mSessionCreateCallback
+ // The previous callbacks will be ignored. For the logic, mSessionCallback
// is newly assigned for every bindTvInput call and compared with
// MySessionCreateCallback.this.
- mSessionCreateCallback = new MySessionCreateCallback(callback);
- mTvInputManager.createSession(inputId, mSessionCreateCallback, mHandler);
+ mSessionCallback = new MySessionCallback(callback);
+ mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
}
/**
@@ -336,16 +336,16 @@ public class TvView extends SurfaceView {
boolean onUnhandledInputEvent(InputEvent event);
}
- private class MySessionCreateCallback implements SessionCreateCallback {
- final SessionCreateCallback mExternalCallback;
+ private class MySessionCallback extends SessionCallback {
+ final SessionCallback mExternalCallback;
- MySessionCreateCallback(SessionCreateCallback externalCallback) {
+ MySessionCallback(SessionCallback externalCallback) {
mExternalCallback = externalCallback;
}
@Override
public void onSessionCreated(Session session) {
- if (this != mSessionCreateCallback) {
+ if (this != mSessionCallback) {
// This callback is obsolete.
session.release();
return;
@@ -364,5 +364,13 @@ public class TvView extends SurfaceView {
mExternalCallback.onSessionCreated(session);
}
}
+
+ @Override
+ public void onSessionReleased(Session session) {
+ mSession = null;
+ if (mExternalCallback != null) {
+ mExternalCallback.onSessionReleased(session);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 5524bcaab97c..05f99475a495 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -225,7 +225,7 @@ public final class TvInputManagerService extends SystemService {
return serviceState;
}
- private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+ private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
UserState userState = getUserStateLocked(userId);
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (sessionState == null) {
@@ -236,6 +236,11 @@ public final class TvInputManagerService extends SystemService {
throw new SecurityException("Illegal access to the session with token " + sessionToken
+ " from uid " + callingUid);
}
+ return sessionState;
+ }
+
+ private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
ITvInputSession session = sessionState.mSession;
if (session == null) {
throw new IllegalStateException("Session not yet created for token " + sessionToken);
@@ -255,6 +260,13 @@ public final class TvInputManagerService extends SystemService {
if (serviceState == null) {
return;
}
+ if (serviceState.mReconnecting) {
+ if (!serviceState.mSessionTokens.isEmpty()) {
+ // wait until all the sessions are removed.
+ return;
+ }
+ serviceState.mReconnecting = false;
+ }
boolean isStateEmpty = serviceState.mClients.isEmpty()
&& serviceState.mSessionTokens.isEmpty();
if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
@@ -307,9 +319,14 @@ public final class TvInputManagerService extends SystemService {
sessionState.mSession = session;
if (session == null) {
removeSessionStateLocked(sessionToken, userId);
- sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null,
- null, sessionState.mSeq, userId);
+ sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
+ null, null, sessionState.mSeq, userId);
} else {
+ try {
+ session.asBinder().linkToDeath(sessionState, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Session is already died.");
+ }
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
sessionToken, channels[0], sessionState.mSeq, userId);
}
@@ -337,11 +354,19 @@ public final class TvInputManagerService extends SystemService {
} catch (RemoteException exception) {
Slog.e(TAG, "error in onSessionCreated", exception);
}
+ }
- if (sessionToken == null) {
- // This means that the session creation failed. We might want to disconnect the service.
- updateServiceConnectionLocked(inputId, userId);
+ private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+ if (sessionState.mSession != null) {
+ try {
+ sessionState.mSession.release();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "session is already disapeared", e);
+ }
+ sessionState.mSession = null;
}
+ removeSessionStateLocked(sessionToken, userId);
}
private void removeSessionStateLocked(IBinder sessionToken, int userId) {
@@ -365,6 +390,18 @@ public final class TvInputManagerService extends SystemService {
updateServiceConnectionLocked(sessionState.mInputId, userId);
}
+ private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
+ for (IBinder iBinder : serviceState.mClients) {
+ ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
+ try {
+ client.onAvailabilityChanged(
+ serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAvailabilityChanged", e);
+ }
+ }
+ }
+
private final class BinderService extends ITvInputManager.Stub {
@Override
public List<TvInputInfo> getTvInputList(int userId) {
@@ -489,22 +526,28 @@ public final class TvInputManagerService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- // Create a new session token and a session state.
- IBinder sessionToken = new Binder();
- SessionState sessionState = new SessionState(inputId, client, seq, callingUid);
- sessionState.mSession = null;
-
- // Add them to the global session state map of the current user.
UserState userState = getUserStateLocked(resolvedUserId);
- userState.sessionStateMap.put(sessionToken, sessionState);
-
- // Also, add them to the session state map of the current service.
ServiceState serviceState = userState.serviceStateMap.get(inputId);
if (serviceState == null) {
serviceState = new ServiceState(
userState.inputMap.get(inputId), resolvedUserId);
userState.serviceStateMap.put(inputId, serviceState);
}
+ // Send a null token immediately while reconnecting.
+ if (serviceState.mReconnecting == true) {
+ sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
+ return;
+ }
+
+ // Create a new session token and a session state.
+ IBinder sessionToken = new Binder();
+ SessionState sessionState = new SessionState(
+ sessionToken, inputId, client, seq, callingUid, resolvedUserId);
+
+ // Add them to the global session state map of the current user.
+ userState.sessionStateMap.put(sessionToken, sessionState);
+
+ // Also, add them to the session state map of the current service.
serviceState.mSessionTokens.add(sessionToken);
if (serviceState.mService != null) {
@@ -527,14 +570,7 @@ public final class TvInputManagerService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- // Release the session.
- try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId).release();
- } catch (RemoteException e) {
- Slog.e(TAG, "error in release", e);
- }
-
- removeSessionStateLocked(sessionToken, resolvedUserId);
+ releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -710,34 +746,57 @@ public final class TvInputManagerService extends SystemService {
}
private final class ServiceState {
+ // TODO: need to implement DeathRecipient for clients.
private final List<IBinder> mClients = new ArrayList<IBinder>();
private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
private final ServiceConnection mConnection;
+ private final TvInputInfo mTvInputInfo;
private ITvInputService mService;
private ServiceCallback mCallback;
private boolean mBound;
private boolean mAvailable;
+ private boolean mReconnecting;
private ServiceState(TvInputInfo inputInfo, int userId) {
- this.mConnection = new InputServiceConnection(inputInfo, userId);
+ mTvInputInfo = inputInfo;
+ mConnection = new InputServiceConnection(inputInfo, userId);
}
}
- private static final class SessionState {
+ private final class SessionState implements IBinder.DeathRecipient {
private final String mInputId;
private final ITvInputClient mClient;
private final int mSeq;
private final int mCallingUid;
-
+ private final int mUserId;
+ private final IBinder mToken;
private ITvInputSession mSession;
private Uri mLogUri;
- private SessionState(String inputId, ITvInputClient client, int seq, int callingUid) {
- this.mInputId = inputId;
- this.mClient = client;
- this.mSeq = seq;
- this.mCallingUid = callingUid;
+ private SessionState(IBinder token, String inputId, ITvInputClient client, int seq,
+ int callingUid, int userId) {
+ mToken = token;
+ mInputId = inputId;
+ mClient = client;
+ mSeq = seq;
+ mCallingUid = callingUid;
+ mUserId = userId;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mSession = null;
+ if (mClient != null) {
+ try {
+ mClient.onSessionReleased(mSeq);
+ } catch(RemoteException e) {
+ Slog.e(TAG, "error in onSessionReleased", e);
+ }
+ }
+ removeSessionStateLocked(mToken, mUserId);
+ }
}
}
@@ -781,6 +840,37 @@ public final class TvInputManagerService extends SystemService {
if (DEBUG) {
Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")");
}
+ if (!mTvInputInfo.getComponent().equals(name)) {
+ throw new IllegalArgumentException("Mismatched ComponentName: "
+ + mTvInputInfo.getComponent() + " (expected), " + name + " (actual).");
+ }
+ synchronized (mLock) {
+ UserState userState = getUserStateLocked(mUserId);
+ ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId());
+ if (serviceState != null) {
+ serviceState.mReconnecting = true;
+ serviceState.mBound = false;
+ serviceState.mService = null;
+ serviceState.mCallback = null;
+
+ // Send null tokens for not finishing create session events.
+ for (IBinder sessionToken : serviceState.mSessionTokens) {
+ SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+ if (sessionState.mSession == null) {
+ removeSessionStateLocked(sessionToken, sessionState.mUserId);
+ sendSessionTokenToClientLocked(sessionState.mClient,
+ sessionState.mInputId, null, null, sessionState.mSeq,
+ sessionState.mUserId);
+ }
+ }
+
+ if (serviceState.mAvailable) {
+ serviceState.mAvailable = false;
+ broadcastServiceAvailabilityChangedLocked(serviceState);
+ }
+ updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
+ }
+ }
}
}
@@ -792,18 +882,16 @@ public final class TvInputManagerService extends SystemService {
}
@Override
- public void onAvailabilityChanged(String inputId, boolean isAvailable)
- throws RemoteException {
+ public void onAvailabilityChanged(String inputId, boolean isAvailable) {
if (DEBUG) {
Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable="
+ isAvailable + ")");
}
synchronized (mLock) {
ServiceState serviceState = getServiceStateLocked(inputId, mUserId);
- serviceState.mAvailable = isAvailable;
- for (IBinder iBinder : serviceState.mClients) {
- ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
- client.onAvailabilityChanged(inputId, isAvailable);
+ if (serviceState.mAvailable != isAvailable) {
+ serviceState.mAvailable = isAvailable;
+ broadcastServiceAvailabilityChangedLocked(serviceState);
}
}
}