Add TvIAppManagerCallback

to handle IApp added/removed/updated

Bug: 205796538
Test: mmm
Change-Id: I384014b7cf26bd75f1f0a9b140aa180fce0d52c3
This commit is contained in:
shubang
2021-11-09 23:23:21 -08:00
parent 9736c2eb4f
commit c524b1f35f
6 changed files with 404 additions and 1 deletions

View File

@@ -17,6 +17,8 @@
package android.media.tv.interactive;
import android.media.tv.interactive.ITvIAppClient;
import android.media.tv.interactive.ITvIAppManagerCallback;
import android.media.tv.interactive.TvIAppInfo;
import android.view.Surface;
/**
@@ -24,6 +26,7 @@ import android.view.Surface;
* @hide
*/
interface ITvIAppManager {
List<TvIAppInfo> getTvIAppServiceList(int userId);
void startIApp(in IBinder sessionToken, int userId);
void createSession(
in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
@@ -31,4 +34,7 @@ interface ITvIAppManager {
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
int userId);
void registerCallback(in ITvIAppManagerCallback callback, int userId);
void unregisterCallback(in ITvIAppManagerCallback callback, int userId);
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.tv.interactive;
import android.media.tv.interactive.TvIAppInfo;
/**
* Interface to receive callbacks from ITvIAppManager regardless of sessions.
* @hide
*/
interface ITvIAppManagerCallback {
void onIAppServiceAdded(in String iAppServiceId);
void onIAppServiceRemoved(in String iAppServiceId);
void onIAppServiceUpdated(in String iAppServiceId);
void onTvIAppInfoUpdated(in TvIAppInfo tvIAppInfo);
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.tv.interactive;
parcelable TvIAppInfo;

View File

@@ -89,6 +89,14 @@ public final class TvIAppInfo implements Parcelable {
return mId;
}
/**
* Returns the component of the TV IApp service.
* @hide
*/
public ComponentName getComponent() {
return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
}
/**
* Returns the information of the service that implements this TV IApp service.
*/

View File

@@ -29,6 +29,10 @@ import android.view.Surface;
import com.android.internal.util.Preconditions;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Central system API to the overall TV interactive application framework (TIAF) architecture, which
* arbitrates interaction between applications and interactive apps.
@@ -45,10 +49,15 @@ public final class TvIAppManager {
private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
new SparseArray<>();
// @GuardedBy("mLock")
private final List<TvIAppCallbackRecord> mCallbackRecords = new LinkedList<>();
// A sequence number for the next session to be created. Should be protected by a lock
// {@code mSessionCallbackRecordMap}.
private int mNextSeq;
private final Object mLock = new Object();
private final ITvIAppClient mClient;
/** @hide */
@@ -102,6 +111,154 @@ public final class TvIAppManager {
}
}
};
ITvIAppManagerCallback managerCallback = new ITvIAppManagerCallback.Stub() {
// TODO: handle IApp service state changes
@Override
public void onIAppServiceAdded(String iAppServiceId) {
synchronized (mLock) {
for (TvIAppCallbackRecord record : mCallbackRecords) {
record.postIAppServiceAdded(iAppServiceId);
}
}
}
@Override
public void onIAppServiceRemoved(String iAppServiceId) {
synchronized (mLock) {
for (TvIAppCallbackRecord record : mCallbackRecords) {
record.postIAppServiceRemoved(iAppServiceId);
}
}
}
@Override
public void onIAppServiceUpdated(String iAppServiceId) {
synchronized (mLock) {
for (TvIAppCallbackRecord record : mCallbackRecords) {
record.postIAppServiceUpdated(iAppServiceId);
}
}
}
@Override
public void onTvIAppInfoUpdated(TvIAppInfo iAppInfo) {
// TODO: add public API updateIAppInfo()
synchronized (mLock) {
for (TvIAppCallbackRecord record : mCallbackRecords) {
record.postTvIAppInfoUpdated(iAppInfo);
}
}
}
};
try {
if (mService != null) {
mService.registerCallback(managerCallback, mUserId);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Callback used to monitor status of the TV IApp.
* @hide
*/
public abstract static class TvIAppCallback {
/**
* This is called when a TV IApp service is added to the system.
*
* <p>Normally it happens when the user installs a new TV IApp service package that
* implements {@link TvIAppService} interface.
*
* @param iAppServiceId The ID of the TV IApp service.
*/
public void onIAppServiceAdded(String iAppServiceId) {
}
/**
* This is called when a TV IApp service is removed from the system.
*
* <p>Normally it happens when the user uninstalls the previously installed TV IApp service
* package.
*
* @param iAppServiceId The ID of the TV IApp service.
*/
public void onIAppServiceRemoved(String iAppServiceId) {
}
/**
* This is called when a TV IApp service is updated on the system.
*
* <p>Normally it happens when a previously installed TV IApp service package is
* re-installed or a newer version of the package exists becomes available/unavailable.
*
* @param iAppServiceId The ID of the TV IApp service.
*/
public void onIAppServiceUpdated(String iAppServiceId) {
}
/**
* This is called when the information about an existing TV IApp service has been updated.
*
* <p>Because the system automatically creates a <code>TvIAppInfo</code> object for each TV
* IApp service based on the information collected from the
* <code>AndroidManifest.xml</code>, this method is only called back when such information
* has changed dynamically.
*
* @param iAppInfo The <code>TvIAppInfo</code> object that contains new information.
*/
public void onTvIAppInfoUpdated(TvIAppInfo iAppInfo) {
}
}
private static final class TvIAppCallbackRecord {
private final TvIAppCallback mCallback;
private final Handler mHandler;
TvIAppCallbackRecord(TvIAppCallback callback, Handler handler) {
mCallback = callback;
mHandler = handler;
}
public TvIAppCallback getCallback() {
return mCallback;
}
public void postIAppServiceAdded(final String iAppServiceId) {
mHandler.post(new Runnable() {
@Override
public void run() {
mCallback.onIAppServiceAdded(iAppServiceId);
}
});
}
public void postIAppServiceRemoved(final String iAppServiceId) {
mHandler.post(new Runnable() {
@Override
public void run() {
mCallback.onIAppServiceRemoved(iAppServiceId);
}
});
}
public void postIAppServiceUpdated(final String iAppServiceId) {
mHandler.post(new Runnable() {
@Override
public void run() {
mCallback.onIAppServiceUpdated(iAppServiceId);
}
});
}
public void postTvIAppInfoUpdated(final TvIAppInfo iAppInfo) {
mHandler.post(new Runnable() {
@Override
public void run() {
mCallback.onTvIAppInfoUpdated(iAppInfo);
}
});
}
}
/**
@@ -138,6 +295,56 @@ public final class TvIAppManager {
}
}
/**
* Returns the complete list of TV IApp service on the system.
*
* @return List of {@link TvIAppInfo} for each TV IApp service that describes its meta
* information.
* @hide
*/
public List<TvIAppInfo> getTvIAppServiceList() {
try {
return mService.getTvIAppServiceList(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Registers a {@link TvIAppManager.TvIAppCallback}.
*
* @param callback A callback used to monitor status of the TV IApp services.
* @param handler A {@link Handler} that the status change will be delivered to.
* @hide
*/
public void registerCallback(@NonNull TvIAppCallback callback, @NonNull Handler handler) {
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(handler);
synchronized (mLock) {
mCallbackRecords.add(new TvIAppCallbackRecord(callback, handler));
}
}
/**
* Unregisters the existing {@link TvIAppManager.TvIAppCallback}.
*
* @param callback The existing callback to remove.
* @hide
*/
public void unregisterCallback(@NonNull final TvIAppCallback callback) {
Preconditions.checkNotNull(callback);
synchronized (mLock) {
for (Iterator<TvIAppCallbackRecord> it = mCallbackRecords.iterator();
it.hasNext(); ) {
TvIAppCallbackRecord record = it.next();
if (record.getCallback() == callback) {
it.remove();
break;
}
}
}
}
/**
* The Session provides the per-session functionality of interactive app.
* @hide

View File

@@ -29,6 +29,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.media.tv.interactive.ITvIAppClient;
import android.media.tv.interactive.ITvIAppManager;
import android.media.tv.interactive.ITvIAppManagerCallback;
import android.media.tv.interactive.ITvIAppService;
import android.media.tv.interactive.ITvIAppServiceCallback;
import android.media.tv.interactive.ITvIAppSession;
@@ -38,9 +39,11 @@ import android.media.tv.interactive.TvIAppService;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Surface;
@@ -145,12 +148,85 @@ public class TvIAppManagerService extends SystemService {
iAppState.mIAppNumber = count;
}
// TODO: notify iApp added / removed
for (String iAppServiceId : iAppMap.keySet()) {
if (!userState.mIAppMap.containsKey(iAppServiceId)) {
notifyIAppServiceAddedLocked(userState, iAppServiceId);
} else if (updatedPackages != null) {
// Notify the package updates
ComponentName component = iAppMap.get(iAppServiceId).mInfo.getComponent();
for (String updatedPackage : updatedPackages) {
if (component.getPackageName().equals(updatedPackage)) {
updateServiceConnectionLocked(component, userId);
notifyIAppServiceUpdatedLocked(userState, iAppServiceId);
break;
}
}
}
}
for (String iAppServiceId : userState.mIAppMap.keySet()) {
if (!iAppMap.containsKey(iAppServiceId)) {
TvIAppInfo info = userState.mIAppMap.get(iAppServiceId).mInfo;
ServiceState serviceState = userState.mServiceStateMap.get(info.getComponent());
if (serviceState != null) {
abortPendingCreateSessionRequestsLocked(serviceState, iAppServiceId, userId);
}
notifyIAppServiceRemovedLocked(userState, iAppServiceId);
}
}
userState.mIAppMap.clear();
userState.mIAppMap = iAppMap;
}
@GuardedBy("mLock")
private void notifyIAppServiceAddedLocked(UserState userState, String iAppServiceId) {
if (DEBUG) {
Slog.d(TAG, "notifyIAppServiceAddedLocked(iAppServiceId=" + iAppServiceId + ")");
}
int n = userState.mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
userState.mCallbacks.getBroadcastItem(i).onIAppServiceAdded(iAppServiceId);
} catch (RemoteException e) {
Slog.e(TAG, "failed to report added IApp service to callback", e);
}
}
userState.mCallbacks.finishBroadcast();
}
@GuardedBy("mLock")
private void notifyIAppServiceRemovedLocked(UserState userState, String iAppServiceId) {
if (DEBUG) {
Slog.d(TAG, "notifyIAppServiceRemovedLocked(iAppServiceId=" + iAppServiceId + ")");
}
int n = userState.mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
userState.mCallbacks.getBroadcastItem(i).onIAppServiceRemoved(iAppServiceId);
} catch (RemoteException e) {
Slog.e(TAG, "failed to report removed IApp service to callback", e);
}
}
userState.mCallbacks.finishBroadcast();
}
@GuardedBy("mLock")
private void notifyIAppServiceUpdatedLocked(UserState userState, String iAppServiceId) {
if (DEBUG) {
Slog.d(TAG, "notifyIAppServiceUpdatedLocked(iAppServiceId=" + iAppServiceId + ")");
}
int n = userState.mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
userState.mCallbacks.getBroadcastItem(i).onIAppServiceUpdated(iAppServiceId);
} catch (RemoteException e) {
Slog.e(TAG, "failed to report updated IApp service to callback", e);
}
}
userState.mCallbacks.finishBroadcast();
}
private int getIAppUid(TvIAppInfo info) {
try {
return getContext().getPackageManager().getApplicationInfo(
@@ -318,6 +394,25 @@ public class TvIAppManagerService extends SystemService {
private final class BinderService extends ITvIAppManager.Stub {
@Override
public List<TvIAppInfo> getTvIAppServiceList(int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "getTvIAppServiceList");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
List<TvIAppInfo> iAppList = new ArrayList<>();
for (TvIAppState state : userState.mIAppMap.values()) {
iAppList.add(state.mInfo);
}
return iAppList;
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void createSession(final ITvIAppClient client, final String iAppServiceId, int type,
int seq, int userId) {
@@ -463,6 +558,40 @@ public class TvIAppManagerService extends SystemService {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void registerCallback(final ITvIAppManagerCallback callback, int userId) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
"registerCallback");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
if (!userState.mCallbacks.register(callback)) {
Slog.e(TAG, "client process has already died");
}
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void unregisterCallback(ITvIAppManagerCallback callback, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "unregisterCallback");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
userState.mCallbacks.unregister(callback);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@GuardedBy("mLock")
@@ -634,6 +763,10 @@ public class TvIAppManagerService extends SystemService {
// A set of all TV IApp service packages.
private final Set<String> mPackageSet = new HashSet<>();
// A list of callbacks.
private final RemoteCallbackList<ITvIAppManagerCallback> mCallbacks =
new RemoteCallbackList<>();
private UserState(int userId) {
mUserId = userId;
}