diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 50b9d6b47e020..8b07418668bad 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -109,6 +109,8 @@ import android.media.session.MediaSessionManager; import android.media.soundtrigger.SoundTriggerManager; import android.media.tv.ITvInputManager; import android.media.tv.TvInputManager; +import android.media.tv.tuner.ITunerResourceManager; +import android.media.tv.tuner.TunerResourceManager; import android.net.ConnectivityDiagnosticsManager; import android.net.ConnectivityManager; import android.net.ConnectivityThread; @@ -937,6 +939,17 @@ public final class SystemServiceRegistry { return new TvInputManager(service, ctx.getUserId()); }}); + registerService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, TunerResourceManager.class, + new CachedServiceFetcher() { + @Override + public TunerResourceManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder iBinder = + ServiceManager.getServiceOrThrow(Context.TV_TUNER_RESOURCE_MGR_SERVICE); + ITunerResourceManager service = ITunerResourceManager.Stub.asInterface(iBinder); + return new TunerResourceManager(service, ctx.getUserId()); + }}); + registerService(Context.NETWORK_SCORE_SERVICE, NetworkScoreManager.class, new CachedServiceFetcher() { @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 249e58244870d..49f62f4078068 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3457,6 +3457,7 @@ public abstract class Context { CONSUMER_IR_SERVICE, //@hide: TRUST_SERVICE, TV_INPUT_SERVICE, + //@hide: TV_TUNER_RESOURCE_MGR_SERVICE, //@hide: NETWORK_SCORE_SERVICE, USAGE_STATS_SERVICE, MEDIA_SESSION_SERVICE, @@ -4756,6 +4757,17 @@ public abstract class Context { */ public static final String TV_INPUT_SERVICE = "tv_input"; + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.media.tv.TunerResourceManager} for interacting with TV + * tuner resources on the device. + * + * @see #getSystemService(String) + * @see android.media.tv.TunerResourceManager + * @hide + */ + public static final String TV_TUNER_RESOURCE_MGR_SERVICE = "tv_tuner_resource_mgr"; + /** * {@link android.net.NetworkScoreManager} for managing network scoring. * @see #getSystemService(String) diff --git a/media/java/android/media/tv/tuner/CasSessionRequest.aidl b/media/java/android/media/tv/tuner/CasSessionRequest.aidl new file mode 100644 index 0000000000000..3dbf3d836ff34 --- /dev/null +++ b/media/java/android/media/tv/tuner/CasSessionRequest.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2020 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.tuner; + +/** + * A wrapper of a cas session requests that contains all the request info of the client. + * + * @hide + */ +parcelable CasSessionRequest; \ No newline at end of file diff --git a/media/java/android/media/tv/tuner/CasSessionRequest.java b/media/java/android/media/tv/tuner/CasSessionRequest.java new file mode 100644 index 0000000000000..0f6a885dec61f --- /dev/null +++ b/media/java/android/media/tv/tuner/CasSessionRequest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2020 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.tuner; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * Information required to request a Cas Session. + * + * @hide + */ +public final class CasSessionRequest implements Parcelable { + static final String TAG = "CasSessionRequest"; + + public static final + @NonNull + Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public CasSessionRequest createFromParcel(Parcel source) { + try { + return new CasSessionRequest(source); + } catch (Exception e) { + Log.e(TAG, "Exception creating CasSessionRequest from parcel", e); + return null; + } + } + + @Override + public CasSessionRequest[] newArray(int size) { + return new CasSessionRequest[size]; + } + }; + + /** + * Client id of the client that sends the request. + */ + private final int mClientId; + + /** + * System id of the requested cas. + */ + private final int mCasSystemId; + + private CasSessionRequest(@NonNull Parcel source) { + mClientId = source.readInt(); + mCasSystemId = source.readInt(); + } + + /** + * Constructs a new {@link CasSessionRequest} with the given parameters. + * + * @param clientId id of the client. + * @param casSystemId the cas system id that the client is requesting. + */ + public CasSessionRequest(int clientId, + int casSystemId) { + mClientId = clientId; + mCasSystemId = casSystemId; + } + + /** + * Returns the id of the client. + */ + public int getClientId() { + return mClientId; + } + + /** + * Returns the cas system id requested. + */ + public int getCasSystemId() { + return mCasSystemId; + } + + // Parcelable + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + StringBuilder b = new StringBuilder(128); + b.append("CasSessionRequest {clientId=").append(mClientId); + b.append(", casSystemId=").append(mCasSystemId); + b.append("}"); + return b.toString(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mClientId); + dest.writeInt(mCasSystemId); + } +} diff --git a/media/java/android/media/tv/tuner/ITunerResourceManager.aidl b/media/java/android/media/tv/tuner/ITunerResourceManager.aidl new file mode 100644 index 0000000000000..758c68949b657 --- /dev/null +++ b/media/java/android/media/tv/tuner/ITunerResourceManager.aidl @@ -0,0 +1,244 @@ +/* + * Copyright 2020 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.tuner; + +import android.media.tv.tuner.CasSessionRequest; +import android.media.tv.tuner.ITunerResourceManagerListener; +import android.media.tv.tuner.ResourceClientProfile; +import android.media.tv.tuner.TunerFrontendInfo; +import android.media.tv.tuner.TunerFrontendRequest; +import android.media.tv.tuner.TunerLnbRequest; + +/** + * Interface of the Tuner Resource Manager. It manages resources used by TV Tuners. + *

Resources include: + *

    + *
  • TunerFrontend {@link android.media.tv.tuner.frontend}. + *
  • TunerLnb {@link android.media.tv.tuner.Lnb}. + *
  • MediaCas {@link android.media.MediaCas}. + *
  • TvInputHardware {@link android.media.tv.TvInputHardwareInfo}. + *
      + * + *

      Expected workflow is: + *

        + *
      • Tuner Java/MediaCas/TIF update resources of the current device with TRM. + *
      • Client registers its profile through {@link #registerClientProfile(ResourceClientProfile, + * ITunerResourceManagerListener, int[])}. + *
      • Client requests resources through request APIs. + *
      • If the resource needs to be handed to a higher priority client from a lower priority + * one, TRM calls ITunerResourceManagerListener registered by the lower priority client to release + * the resource. + *
          + * + * @hide + */ +interface ITunerResourceManager { + /* + * This API is used by the client to register their profile with the Tuner Resource manager. + * + *

          The profile contains information that can show the base priority score of the client. + * + * @param profile {@link ResourceClientProfile} profile of the current client + * @param listener {@link ITunerResourceManagerListener} a callback to + * reclaim clients' resources when needed. + * @param clientId returns a clientId from the resource manager when the + * the client registers its profile. + */ + void registerClientProfile(in ResourceClientProfile profile, + ITunerResourceManagerListener listener, out int[] clientId); + + /* + * This API is used by the client to unregister their profile with the Tuner Resource manager. + * + * @param clientId the client id that needs to be unregistered. + */ + void unregisterClientProfile(in int clientId); + + /* + * Updates a registered client's priority and niceValue. + * + * @param clientId the id of the client that is updating its profile. + * @param priority the priority that the client would like to update to. + * @param niceValue the nice value that the client would like to update to. + * + * @return true if the update is successful. + */ + boolean updateClientPriority(in int clientId, in int priority, in int niceValue); + + /* + * Updates the available Frontend resources information on the current device. + * + *

          Note: This update must happen before the first + * {@link #requestFrontend(TunerFrontendRequest,int[])} and {@link #releaseFrontend(int)} call. + * + * @param infos an array of the available {@link TunerFrontendInfo} information. + */ + void setFrontendInfoList(in TunerFrontendInfo[] infos); + + /* + * Updates the available Cas resource information on the current device. + * + *

          Note: This update must happen before the first + * {@link #requestCasSession(CasSessionRequest, int[])} and {@link #releaseCasSession(int)} call. + * + * @param casSystemId id of the updating CAS system. + * @param maxSessionNum the max session number of the CAS system that is updated. + */ + void updateCasInfo(in int casSystemId, in int maxSessionNum); + + /* + * Updates the available Lnb resource information on the current device. + * + *

          Note: This update must happen before the first + * {@link #requestLnb(TunerLnbRequest, int[])} and {@link #releaseLnb(int)} call. + * + * @param lnbIds ids of the updating lnbs. + */ + void setLnbInfoList(in int[] lnbIds); + + /* + * This API is used by the Tuner framework to request an available frontend from the TunerHAL. + * + *

          There are three possible scenarios: + *

            + *
          • If there is frontend available, the API would send the id back. + * + *
          • If no Frontend is available but the current request info can show higher priority than + * other uses of Frontend, the API will send + * {@link ITunerResourceManagerListener#onResourcesReclaim()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + *
          • If no frontend can be granted, the API would return false. + *
              + * + *

              Note: {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called + * before this request. + * + * @param request {@link TunerFrontendRequest} information of the current request. + * @param frontendId a one-element array to return the granted frontendId. + * + * @return true if there is frontend granted. + */ + boolean requestFrontend(in TunerFrontendRequest request, out int[] frontendId); + + /* + * Requests to share frontend with an existing client. + * + *

              Note: {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called + * before this request. + * + * @param selfClientId the id of the client that sends the request. + * @param targetClientId the id of the client to share the frontend with. + */ + void shareFrontend(in int selfClientId, in int targetClientId); + + /* + * This API is used by the Tuner framework to request an available Cas session. This session + * needs to be under the CAS system with the id indicated in the {@code request}. + * + *

              There are three possible scenarios: + *

                + *
              • If there is Cas session available, the API would send the id back. + * + *
              • If no Cas session is available but the current request info can show higher priority than + * other uses of the sessions under the requested CAS system, the API will send + * {@link ITunerResourceManagerCallback#onResourcesReclaim()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + *
              • If no Cas session can be granted, the API would return false. + *
                  + * + *

                  Note: {@link #updateCasInfo(int, int)} must be called before this request. + * + * @param request {@link CasSessionRequest} information of the current request. + * @param sessionResourceId a one-element array to return the granted cas session id. + * + * @return true if there is CAS session granted. + */ + boolean requestCasSession(in CasSessionRequest request, out int[] sessionResourceId); + + /* + * This API is used by the Tuner framework to request an available Lnb from the TunerHAL. + * + *

                  There are three possible scenarios: + *

                    + *
                  • If there is Lnb available, the API would send the id back. + * + *
                  • If no Lnb is available but the current request has a higher priority than other uses of + * lnbs, the API will send {@link ITunerResourceManagerCallback#onResourcesReclaim()} to the + * {@link Tuner}. Tuner would handle the resource reclaim on the holder of lower priority and + * notify the holder of its resource loss. + * + *
                  • If no Lnb system can be granted, the API would return false. + *
                      + * + *

                      Note: {@link #setLnbInfos(int[])} must be called before this request. + * + * @param request {@link TunerLnbRequest} information of the current request. + * @param lnbId a one-element array to return the granted Lnb id. + * + * @return true if there is Lnb granted. + */ + boolean requestLnb(in TunerLnbRequest request, out int[] lnbId); + + /* + * Notifies the TRM that the given frontend has been released. + * + *

                      Client must call this whenever it releases a Tuner frontend. + * + *

                      Note: {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called + * before this release. + * + * @param frontendId the id of the released frontend. + */ + void releaseFrontend(in int frontendId); + + /* + * Notifies the TRM that the given Cas session has been released. + * + *

                      Client must call this whenever it releases a Cas session. + * + *

                      Note: {@link #updateCasInfo(int, int)} must be called before this release. + * + * @param sessionResourceId the id of the released CAS session. + */ + void releaseCasSession(in int sessionResourceId); + + /* + * Notifies the TRM that the Lnb with the given id was released. + * + *

                      Client must call this whenever it releases an Lnb. + * + *

                      Note: {@link #setLnbInfos(int[])} must be called before this release. + * + * @param lnbId the id of the released Tuner Lnb. + */ + void releaseLnb(in int lnbId); + + /* + * Compare two clients' priority. + * + * @param challengerProfile the {@link ResourceClientProfile} of the challenger. + * @param holderProfile the {@link ResourceClientProfile} of the holder of the resource. + * + * @return true if the challenger has higher priority than the holder. + */ + boolean isHigherPriority(in ResourceClientProfile challengerProfile, + in ResourceClientProfile holderProfile); +} diff --git a/media/java/android/media/tv/tuner/ITunerResourceManagerListener.aidl b/media/java/android/media/tv/tuner/ITunerResourceManagerListener.aidl new file mode 100644 index 0000000000000..557032ca8d6d7 --- /dev/null +++ b/media/java/android/media/tv/tuner/ITunerResourceManagerListener.aidl @@ -0,0 +1,33 @@ +/* + * Copyright 2020 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.tuner; + +/** + * Interface to receive callbacks from ITunerResourceManager. + * + * @hide + */ +oneway interface ITunerResourceManagerListener { + /* + * TRM invokes this method when the client's resources need to be reclaimed. + * + *

                      This method is implemented in Tuner Framework to take the reclaiming + * actions. It's a synchonized call. TRM would wait on the call to finish + * then grant the resource. + */ + void onResourcesReclaim(); +} \ No newline at end of file diff --git a/media/java/android/media/tv/tuner/ResourceClientProfile.aidl b/media/java/android/media/tv/tuner/ResourceClientProfile.aidl new file mode 100644 index 0000000000000..da3c5c4f15f92 --- /dev/null +++ b/media/java/android/media/tv/tuner/ResourceClientProfile.aidl @@ -0,0 +1,25 @@ +/* + * Copyright 2020 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.tuner; + +/** + * A profile of a resource client. This profile is used to register the client info + * with the Tuner Resource Manager. + * + * @hide + */ +parcelable ResourceClientProfile; \ No newline at end of file diff --git a/media/java/android/media/tv/tuner/ResourceClientProfile.java b/media/java/android/media/tv/tuner/ResourceClientProfile.java new file mode 100644 index 0000000000000..e2031353b27a4 --- /dev/null +++ b/media/java/android/media/tv/tuner/ResourceClientProfile.java @@ -0,0 +1,129 @@ +/* + * Copyright 2020 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.tuner; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * A profile of a resource client. This profile is used to register the client info + * with the Tuner Resource Manager(TRM). + * + * @hide + */ +public final class ResourceClientProfile implements Parcelable { + static final String TAG = "ResourceClientProfile"; + + public static final + @NonNull + Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public ResourceClientProfile createFromParcel(Parcel source) { + try { + return new ResourceClientProfile(source); + } catch (Exception e) { + Log.e(TAG, "Exception creating ResourceClientProfile from parcel", e); + return null; + } + } + + @Override + public ResourceClientProfile[] newArray(int size) { + return new ResourceClientProfile[size]; + } + }; + + /** + * This is used by TRM to get TV App’s processId from TIF. + * The processId will be used to identify foreground applications. + * + *

                      MediaCas, Tuner and TvInputHardwareManager get tvInputSessionId from TIS. + * If mTvInputSessionId is UNKNOWN, the client is always background. + */ + private final String mTvInputSessionId; + + /** + * Usage of the client. + */ + private final int mUseCase; + + private ResourceClientProfile(@NonNull Parcel source) { + mTvInputSessionId = source.readString(); + mUseCase = source.readInt(); + } + + /** + * Constructs a new {@link ResourceClientProfile} with the given parameters. + * + * @param tvInputSessionId the unique id of the session owned by the client. + * @param useCase the usage of the client. Suggested priority hints are + * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK} + * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE} + * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD}. + * New [use case : priority value] pair can be defined in the manifest by the + * OEM. Any undefined use case would cause IllegalArgumentException. + */ + public ResourceClientProfile(@NonNull String tvInputSessionId, + int useCase) { + mTvInputSessionId = tvInputSessionId; + mUseCase = useCase; + } + + /** + * Returns the tv input session id of the client. + * + * @return the value of the tv input session id. + */ + @NonNull + public String getTvInputSessionId() { + return mTvInputSessionId; + } + + /** + * Returns the user usage of the client. + * + * @return the value of use case. + */ + public int getUseCase() { + return mUseCase; + } + + // Parcelable + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + StringBuilder b = new StringBuilder(128); + b.append("ResourceClientProfile {tvInputSessionId=").append(mTvInputSessionId); + b.append(", useCase=").append(mUseCase); + b.append("}"); + return b.toString(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mTvInputSessionId); + dest.writeInt(mUseCase); + } +} diff --git a/media/java/android/media/tv/tuner/TunerFrontendInfo.aidl b/media/java/android/media/tv/tuner/TunerFrontendInfo.aidl new file mode 100644 index 0000000000000..012e051d85980 --- /dev/null +++ b/media/java/android/media/tv/tuner/TunerFrontendInfo.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2020 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.tuner; + +/** + * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface. + * + * @hide + */ +parcelable TunerFrontendInfo; \ No newline at end of file diff --git a/media/java/android/media/tv/tuner/TunerFrontendInfo.java b/media/java/android/media/tv/tuner/TunerFrontendInfo.java new file mode 100644 index 0000000000000..a62ecb3893ea7 --- /dev/null +++ b/media/java/android/media/tv/tuner/TunerFrontendInfo.java @@ -0,0 +1,141 @@ +/* + * Copyright 2020 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.tuner; + +import android.annotation.NonNull; +import android.media.tv.tuner.frontend.FrontendSettings.Type; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface. + * + *

                      Note that this object is defined to pass necessary frontend info between the + * Tuner Resource Manager and the client. It includes partial information in + * {@link FrontendInfo}. + * + * @hide + */ +public final class TunerFrontendInfo implements Parcelable { + static final String TAG = "TunerFrontendInfo"; + + public static final + @NonNull + Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public TunerFrontendInfo createFromParcel(Parcel source) { + try { + return new TunerFrontendInfo(source); + } catch (Exception e) { + Log.e(TAG, "Exception creating TunerFrontendInfo from parcel", e); + return null; + } + } + + @Override + public TunerFrontendInfo[] newArray(int size) { + return new TunerFrontendInfo[size]; + } + }; + + private final int mId; + + @Type + private final int mFrontendType; + + /** + * Frontends are assigned with the same exclusiveGroupId if they can't + * function at same time. For instance, they share same hardware module. + */ + private final int mExclusiveGroupId; + + private TunerFrontendInfo(@NonNull Parcel source) { + mId = source.readInt(); + mFrontendType = source.readInt(); + mExclusiveGroupId = source.readInt(); + } + + /** + * Constructs a new {@link TunerFrontendInfo} with the given parameters. + * + * @param frontendType the type of the frontend. + * @param exclusiveGroupId the group id of the frontend. FE with the same + group id can't function at the same time. + */ + public TunerFrontendInfo(int id, + @Type int frontendType, + int exclusiveGroupId) { + mId = id; + mFrontendType = frontendType; + mExclusiveGroupId = exclusiveGroupId; + } + + /** + * Returns the frontend id. + * + * @return the value of the frontend id. + */ + public int getId() { + return mId; + } + + /** + * Returns the application id that requests the tuner frontend resource. + * + * @return the value of the frontend type. + */ + @Type + public int getFrontendType() { + return mFrontendType; + } + + /** + * Returns the exclusiveGroupId. Frontends with the same exclusiveGroupId + * can't function at same time. + * + * @return the value of the exclusive group id. + */ + public int getExclusiveGroupId() { + return mExclusiveGroupId; + } + + // Parcelable + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + StringBuilder b = new StringBuilder(128); + b.append("TunerFrontendInfo {id=").append(mId); + b.append(", frontendType=").append(mFrontendType); + b.append(", exclusiveGroupId=").append(mExclusiveGroupId); + b.append("}"); + return b.toString(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mId); + dest.writeInt(mFrontendType); + dest.writeInt(mExclusiveGroupId); + } +} diff --git a/media/java/android/media/tv/tuner/TunerFrontendRequest.aidl b/media/java/android/media/tv/tuner/TunerFrontendRequest.aidl new file mode 100644 index 0000000000000..25c298f0f65b3 --- /dev/null +++ b/media/java/android/media/tv/tuner/TunerFrontendRequest.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2020 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.tuner; + +/** + * Information required to request a Tuner Frontend. + * + * @hide + */ +parcelable TunerFrontendRequest; \ No newline at end of file diff --git a/media/java/android/media/tv/tuner/TunerFrontendRequest.java b/media/java/android/media/tv/tuner/TunerFrontendRequest.java new file mode 100644 index 0000000000000..01a0a091877fa --- /dev/null +++ b/media/java/android/media/tv/tuner/TunerFrontendRequest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2020 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.tuner; + +import android.annotation.NonNull; +import android.media.tv.tuner.frontend.FrontendSettings.Type; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * Information required to request a Tuner Frontend. + * + * @hide + */ +public final class TunerFrontendRequest implements Parcelable { + static final String TAG = "TunerFrontendRequest"; + + public static final + @NonNull + Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public TunerFrontendRequest createFromParcel(Parcel source) { + try { + return new TunerFrontendRequest(source); + } catch (Exception e) { + Log.e(TAG, "Exception creating TunerFrontendRequest from parcel", e); + return null; + } + } + + @Override + public TunerFrontendRequest[] newArray(int size) { + return new TunerFrontendRequest[size]; + } + }; + + private final int mClientId; + @Type + private final int mFrontendType; + + private TunerFrontendRequest(@NonNull Parcel source) { + mClientId = source.readInt(); + mFrontendType = source.readInt(); + } + + /** + * Constructs a new {@link TunerFrontendRequest} with the given parameters. + * + * @param clientId the unique id of the client returned when registering profile. + * @param frontendType the type of the requested frontend. + */ + public TunerFrontendRequest(int clientId, + @Type int frontendType) { + mClientId = clientId; + mFrontendType = frontendType; + } + + /** + * Returns the client id that requests the tuner frontend resource. + * + * @return the value of the client id. + */ + public int getClientId() { + return mClientId; + } + + /** + * Returns the frontend type that the client requests for. + * + * @return the value of the requested frontend type. + */ + @Type + public int getFrontendType() { + return mFrontendType; + } + + // Parcelable + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + StringBuilder b = new StringBuilder(128); + b.append("TunerFrontendRequest {clientId=").append(mClientId); + b.append(", frontendType=").append(mFrontendType); + b.append("}"); + return b.toString(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mClientId); + dest.writeInt(mFrontendType); + } +} diff --git a/media/java/android/media/tv/tuner/TunerLnbRequest.aidl b/media/java/android/media/tv/tuner/TunerLnbRequest.aidl new file mode 100644 index 0000000000000..b811e3922252f --- /dev/null +++ b/media/java/android/media/tv/tuner/TunerLnbRequest.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2020 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.tuner; + +/** + * Information required to request a Tuner Lnb. + * + * @hide + */ +parcelable TunerLnbRequest; \ No newline at end of file diff --git a/media/java/android/media/tv/tuner/TunerLnbRequest.java b/media/java/android/media/tv/tuner/TunerLnbRequest.java new file mode 100644 index 0000000000000..60cd7903b1761 --- /dev/null +++ b/media/java/android/media/tv/tuner/TunerLnbRequest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 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.tuner; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * Information required to request a Tuner Lnb. + * + * @hide + */ +public final class TunerLnbRequest implements Parcelable { + static final String TAG = "TunerLnbRequest"; + + public static final + @NonNull + Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public TunerLnbRequest createFromParcel(Parcel source) { + try { + return new TunerLnbRequest(source); + } catch (Exception e) { + Log.e(TAG, "Exception creating TunerLnbRequest from parcel", e); + return null; + } + } + + @Override + public TunerLnbRequest[] newArray(int size) { + return new TunerLnbRequest[size]; + } + }; + + /** + * Client id of the client that sends the request. + */ + private final int mClientId; + + private TunerLnbRequest(@NonNull Parcel source) { + mClientId = source.readInt(); + } + + /** + * Constructs a new {@link TunerLnbRequest} with the given parameters. + * + * @param clientId the id of the client. + */ + public TunerLnbRequest(int clientId) { + mClientId = clientId; + } + + /** + * Returns the id of the client + */ + public int getClientId() { + return mClientId; + } + + // Parcelable + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + StringBuilder b = new StringBuilder(128); + b.append("TunerLnbRequest {clientId=").append(mClientId); + b.append("}"); + return b.toString(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mClientId); + } +} diff --git a/media/java/android/media/tv/tuner/TunerResourceManager.java b/media/java/android/media/tv/tuner/TunerResourceManager.java new file mode 100644 index 0000000000000..68ca5722ecad6 --- /dev/null +++ b/media/java/android/media/tv/tuner/TunerResourceManager.java @@ -0,0 +1,407 @@ +/* + * Copyright 2020 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.tuner; + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresFeature; +import android.annotation.SystemService; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.concurrent.Executor; + +/** + * Interface of the Tuner Resource Manager(TRM). It manages resources used by TV Tuners. + *

                      Resources include: + *

                        + *
                      • TunerFrontend {@link android.media.tv.tuner.frontend}. + *
                      • TunerLnb {@link android.media.tv.tuner.Lnb}. + *
                      • MediaCas {@link android.media.MediaCas}. + *
                          + * + *

                          Expected workflow is: + *

                            + *
                          • Tuner Java/MediaCas/TIF update resources of the current device with TRM. + *
                          • Client registers its profile through {@link #registerClientProfile(ResourceClientProfile, + * Executor, ResourceListener, int[])}. + *
                          • Client requests resources through request APIs. + *
                          • If the resource needs to be handed to a higher priority client from a lower priority + * one, TRM calls ITunerResourceManagerListener registered by the lower priority client to release + * the resource. + *
                              + * + *

                              TRM also exposes its priority comparison algorithm as a helping method to other services. + * {@see #isHigherPriority(ResourceClientProfile, ResourceClientProfile)}. + * + * @hide + */ +@RequiresFeature(PackageManager.FEATURE_LIVE_TV) +@SystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE) +public class TunerResourceManager { + private static final String TAG = "TunerResourceManager"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static final int INVALID_FRONTEND_ID = -1; + public static final int INVALID_CAS_SESSION_RESOURCE_ID = -1; + public static final int INVALID_LNB_ID = -1; + public static final int INVALID_TV_INPUT_DEVICE_ID = -1; + public static final int INVALID_TV_INPUT_PORT_ID = -1; + + private final ITunerResourceManager mService; + private final int mUserId; + + /** + * @hide + */ + public TunerResourceManager(ITunerResourceManager service, int userId) { + mService = service; + mUserId = userId; + } + + /** + * This API is used by the client to register their profile with the Tuner Resource manager. + * + *

                              The profile contains information that can show the base priority score of the client. + * + * @param profile {@link ResourceClientProfile} profile of the current client. Undefined use + * case would cause IllegalArgumentException. + * @param executor the executor on which the listener would be invoked. + * @param listener {@link ResourceListener} callback to reclaim clients' resources when needed. + * @param clientId returned a clientId from the resource manager when the + * the client registeres. + * @throws IllegalArgumentException when {@code profile} contains undefined use case. + */ + public void registerClientProfile(@NonNull ResourceClientProfile profile, + @NonNull @CallbackExecutor Executor executor, + @NonNull ResourceListener listener, + @NonNull int[] clientId) { + // TODO: throw new IllegalArgumentException("Unknown client use case") + // when the use case is not defined. + try { + mService.registerClientProfile(profile, + new ITunerResourceManagerListener.Stub() { + @Override + public void onResourcesReclaim() { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> listener.onResourcesReclaim()); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }, clientId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * This API is used by the client to unregister their profile with the + * Tuner Resource manager. + * + * @param clientId the client id that needs to be unregistered. + */ + public void unregisterClientProfile(int clientId) { + try { + mService.unregisterClientProfile(clientId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * This API is used by client to update its registered {@link ResourceClientProfile}. + * + *

                              We recommend creating a new tuner instance for different use cases instead of using this + * API since different use cases may need different resources. + * + *

                              If TIS updates use case, it needs to ensure underneath resources are exchangeable between + * two different use cases. + * + *

                              Only the arbitrary priority and niceValue are allowed to be updated. + * + * @param clientId the id of the client that is updating its profile. + * @param priority the priority that the client would like to update to. + * @param niceValue the nice value that the client would like to update to. + * + * @return true if the update is successful. + */ + public boolean updateClientPriority(int clientId, int priority, int niceValue) { + boolean result = false; + try { + result = mService.updateClientPriority(clientId, priority, niceValue); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return result; + } + + /** + * Updates the current TRM of the TunerHAL Frontend information. + * + *

                              Note: This update must happen before the first + * {@link #requestFrontend(TunerFrontendRequest, int[])} and {@link #releaseFrontend(int)} call. + * + * @param infos an array of the available {@link TunerFrontendInfo} information. + */ + public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) { + try { + mService.setFrontendInfoList(infos); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Updates the TRM of the current CAS information. + * + *

                              Note: This update must happen before the first + * {@link #requestCasSession(CasSessionRequest, int[])} and {@link #releaseCasSession(int)} + * call. + * + * @param casSystemId id of the updating CAS system. + * @param maxSessionNum the max session number of the CAS system that is updated. + */ + public void updateCasInfo(int casSystemId, int maxSessionNum) { + try { + mService.updateCasInfo(casSystemId, maxSessionNum); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Updates the TRM of the current Lnb information. + * + *

                              Note: This update must happen before the first + * {@link #requestLnb(TunerLnbRequest, int[])} and {@link #releaseLnb(int)} call. + * + * @param lnbIds ids of the updating lnbs. + */ + public void setLnbInfoList(int[] lnbIds) { + try { + mService.setLnbInfoList(lnbIds); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Requests a frontend resource. + * + *

                              There are three possible scenarios: + *

                                + *
                              • If there is frontend available, the API would send the id back. + * + *
                              • If no Frontend is available but the current request info can show higher priority than + * other uses of Frontend, the API will send + * {@link ITunerResourceManagerListener#onResourcesReclaim()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + *
                              • If no frontend can be granted, the API would return false. + *
                                  + * + *

                                  Note: {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called + * before this request. + * + * @param request {@link TunerFrontendRequest} information of the current request. + * @param frontendId a one-element array to return the granted frontendId. If + * no frontend granted, this will return {@link #INVALID_FRONTEND_ID}. + * + * @return true if there is frontend granted. + */ + public boolean requestFrontend(@NonNull TunerFrontendRequest request, + @Nullable int[] frontendId) { + boolean result = false; + try { + result = mService.requestFrontend(request, frontendId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return result; + } + + /** + * Requests from the client to share frontend with an existing client. + * + *

                                  Note: {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called + * before this request. + * + * @param selfClientId the id of the client that sends the request. + * @param targetClientId the id of the client to share the frontend with. + */ + public void shareFrontend(int selfClientId, int targetClientId) { + try { + mService.shareFrontend(selfClientId, targetClientId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Requests a CAS session resource. + * + *

                                  There are three possible scenarios: + *

                                    + *
                                  • If there is Cas session available, the API would send the id back. + * + *
                                  • If no Cas system is available but the current request info can show higher priority than + * other uses of the cas sessions under the requested cas system, the API will send + * {@link ITunerResourceManagerListener#onResourcesReclaim()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + *

                                    Note: {@link #updateCasInfo(int, int)} must be called before this + * request. + * + * @param request {@link CasSessionRequest} information of the current request. + * @param sessionResourceId a one-element array to return the granted cas session id. + * If no CAS granted, this will return + * {@link #INVALID_CAS_SESSION_RESOURCE_ID}. + * + * @return true if there is CAS session granted. + */ + public boolean requestCasSession(@NonNull CasSessionRequest request, + @NonNull int[] sessionResourceId) { + boolean result = false; + try { + result = mService.requestCasSession(request, sessionResourceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return result; + } + + /** + * Requests a Tuner Lnb resource. + * + *

                                    There are three possible scenarios: + *

                                      + *
                                    • If there is Lnb available, the API would send the id back. + * + *
                                    • If no Lnb is available but the current request has a higher priority than other uses of + * lnbs, the API will send {@link ITunerResourceManagerListener#onResourcesReclaim()} to the + * {@link Tuner}. Tuner would handle the resource reclaim on the holder of lower priority and + * notify the holder of its resource loss. + * + *
                                    • If no Lnb system can be granted, the API would return false. + *
                                        + * + *

                                        Note: {@link #setLnbInfos(int[])} must be called before this request. + * + * @param request {@link TunerLnbRequest} information of the current request. + * @param lnbId a one-element array to return the granted Lnb id. + * If no Lnb granted, this will return {@link #INVALID_LNB_ID}. + * + * @return true if there is Lnb granted. + */ + public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbId) { + boolean result = false; + try { + result = mService.requestLnb(request, lnbId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return result; + } + + /** + * Notifies the TRM that the given frontend has been released. + * + *

                                        Client must call this whenever it releases a Tuner frontend. + * + *

                                        Note: {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called + * before this release. + * + * @param frontendId the id of the released frontend. + */ + public void releaseFrontend(int frontendId) { + try { + mService.releaseFrontend(frontendId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Notifies the TRM that the given Cas session has been released. + * + *

                                        Client must call this whenever it releases a Cas session. + * + *

                                        Note: {@link #updateCasInfo(int, int)} must be called before this + * release. + * + * @param sessionResourceId the id of the released CAS session. + */ + public void releaseCasSession(int sessionResourceId) { + try { + mService.releaseCasSession(sessionResourceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Notifies the TRM that the Lnb with the given id has been released. + * + *

                                        Client must call this whenever it releases an Lnb. + * + *

                                        Note: {@link #setLnbInfos(int[])} must be called before this release. + * + * @param lnbId the id of the released Tuner Lnb. + */ + public void releaseLnb(int lnbId) { + try { + mService.releaseLnb(lnbId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Compare two clients' priority. + * + * @param challengerProfile the {@link ResourceClientProfile} of the challenger. + * @param holderProfile the {@link ResourceClientProfile} of the holder of the resource. + * + * @return true if the challenger has higher priority than the holder. + */ + public boolean isHigherPriority(ResourceClientProfile challengerProfile, + ResourceClientProfile holderProfile) { + try { + return mService.isHigherPriority(challengerProfile, holderProfile); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Interface used to receive events from TunerResourceManager. + */ + public abstract static class ResourceListener { + /* + * To reclaim all the resources of the callack owner. + */ + public abstract void onResourcesReclaim(); + } +} diff --git a/services/core/java/com/android/server/tv/tuner/ClientProfile.java b/services/core/java/com/android/server/tv/tuner/ClientProfile.java new file mode 100644 index 0000000000000..3845195e6643b --- /dev/null +++ b/services/core/java/com/android/server/tv/tuner/ClientProfile.java @@ -0,0 +1,209 @@ +/* + * Copyright 2020 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 com.android.server.tv.tuner; + +/** + * A client profile object used by the Tuner Resource Manager to record the registered clients' + * information. + * + * @hide + */ +public final class ClientProfile { + public static final int INVALID_GROUP_ID = -1; + /** + * Client id sent to the client when registering with + * {@link #registerClientProfile(ResourceClientProfile, TunerResourceManagerCallback, int[])} + */ + private final int mClientId; + + /** + * see {@link ResourceClientProfile} + */ + private final String mTvInputSessionId; + + /** + * see {@link ResourceClientProfile} + */ + private final int mUseCase; + + /** + * Process id queried from {@link TvInputManager#} + */ + private final int mProcessId; + + /** + * All the clients that share the same resource would be under the same group id. + * + *

                                        If a client's resource is to be reclaimed, all other clients under the same group id + * also lose their resources. + */ + private int mGroupId = INVALID_GROUP_ID; + + /** + * Optional nice value for TRM to reduce client’s priority. + */ + private int mNiceValue; + + /** + * Optional arbitrary priority value given by the client. + * + *

                                        This value can override the default priorotiy calculated from + * the client profile. + */ + private int mPriority; + + private ClientProfile(ClientProfileBuilder builder) { + this.mClientId = builder.mClientId; + this.mTvInputSessionId = builder.mTvInputSessionId; + this.mUseCase = builder.mUseCase; + this.mProcessId = builder.mProcessId; + this.mGroupId = builder.mGroupId; + this.mNiceValue = builder.mNiceValue; + this.mPriority = builder.mPriority; + } + + public int getClientId() { + return mClientId; + } + + public String getTvInputSessionId() { + return mTvInputSessionId; + } + + public int getUseCase() { + return mUseCase; + } + + public int getProcessId() { + return mProcessId; + } + + public int getGroupId() { + return mGroupId; + } + + public int getPriority() { + return mPriority; + } + + public int getNiceValue() { + return mNiceValue; + } + + public void setGroupId(int groupId) { + mGroupId = groupId; + } + + public void setPriority(int priority) { + mPriority = priority; + } + + public void setNiceValue(int niceValue) { + mNiceValue = niceValue; + } + + @Override + public String toString() { + return "ClientProfile: " + this.mClientId + ", " + this.mTvInputSessionId + ", " + + this.mUseCase + ", " + this.mProcessId; + } + + public static class ClientProfileBuilder { + private final int mClientId; + private String mTvInputSessionId; + private int mUseCase; + private int mProcessId; + private int mGroupId; + private int mNiceValue; + private int mPriority; + + ClientProfileBuilder(int clientId) { + this.mClientId = clientId; + } + + /** + * Builder for {@link ClientProfile}. + * + * @param useCase the useCase of the client. + */ + public ClientProfileBuilder useCase(int useCase) { + this.mUseCase = useCase; + return this; + } + + /** + * Builder for {@link ClientProfile}. + * + * @param tvInputSessionId the id of the tv input session. + */ + public ClientProfileBuilder tvInputSessionId(String tvInputSessionId) { + this.mTvInputSessionId = tvInputSessionId; + return this; + } + + /** + * Builder for {@link ClientProfile}. + * + * @param processId the id of process. + */ + public ClientProfileBuilder processId(int processId) { + this.mProcessId = processId; + return this; + } + + + /** + * Builder for {@link ClientProfile}. + * + * @param groupId the id of the group that shares the same resource. + */ + public ClientProfileBuilder groupId(int groupId) { + this.mGroupId = groupId; + return this; + } + + /** + * Builder for {@link ClientProfile}. + * + * @param niceValue the nice value of the client. + */ + public ClientProfileBuilder niceValue(int niceValue) { + this.mNiceValue = niceValue; + return this; + } + + /** + * Builder for {@link ClientProfile}. + * + * @param priority the priority value of the client. + */ + public ClientProfileBuilder priority(int priority) { + this.mPriority = priority; + return this; + } + + /** + * Build a {@link ClientProfile}. + * + * @return {@link ClientProfile}. + */ + public ClientProfile build() { + ClientProfile clientProfile = new ClientProfile(this); + return clientProfile; + } + } +} diff --git a/services/core/java/com/android/server/tv/tuner/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tuner/TunerResourceManagerService.java new file mode 100644 index 0000000000000..e8764214ca210 --- /dev/null +++ b/services/core/java/com/android/server/tv/tuner/TunerResourceManagerService.java @@ -0,0 +1,258 @@ +/* + * Copyright 2020 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 com.android.server.tv.tuner; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.media.tv.TvInputManager; +import android.media.tv.tuner.CasSessionRequest; +import android.media.tv.tuner.ITunerResourceManager; +import android.media.tv.tuner.ITunerResourceManagerListener; +import android.media.tv.tuner.ResourceClientProfile; +import android.media.tv.tuner.TunerFrontendInfo; +import android.media.tv.tuner.TunerFrontendRequest; +import android.media.tv.tuner.TunerLnbRequest; +import android.media.tv.tuner.TunerResourceManager; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.server.SystemService; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides a system service that manages the TV tuner resources. + * + * @hide + */ +public class TunerResourceManagerService extends SystemService { + private static final String TAG = "TunerResourceManagerService"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private SparseArray mClientProfiles = new SparseArray<>(); + private SparseArray mListeners = new SparseArray<>(); + private int mNextUnusedFrontendId = 0; + private List mReleasedClientId = new ArrayList(); + private List mAvailableFrontendIds = new ArrayList(); + + private TvInputManager mManager; + + public TunerResourceManagerService(@Nullable Context context) { + super(context); + } + + @Override + public void onStart() { + publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService()); + mManager = (TvInputManager) getContext() + .getSystemService(Context.TV_INPUT_SERVICE); + } + + private final class BinderService extends ITunerResourceManager.Stub { + @Override + public void registerClientProfile(@NonNull ResourceClientProfile profile, + @NonNull ITunerResourceManagerListener listener, + @NonNull int[] clientId) { + if (DEBUG) { + Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")"); + } + + // TODO tell if the client already exists + if (mReleasedClientId.isEmpty()) { + clientId[0] = mNextUnusedFrontendId++; + } else { + clientId[0] = mReleasedClientId.get(0); + mReleasedClientId.remove(0); + } + + if (mManager == null) { + Slog.e(TAG, "TvInputManager is null. Can't register client profile."); + return; + } + + int callingPid = mManager.getClientPid(profile.getTvInputSessionId()); + + ClientProfile clientProfile = new ClientProfile.ClientProfileBuilder( + clientId[0]) + .tvInputSessionId(profile.getTvInputSessionId()) + .useCase(profile.getUseCase()) + .processId(callingPid) + .build(); + mClientProfiles.append(clientId[0], clientProfile); + mListeners.append(clientId[0], listener); + } + + @Override + public void unregisterClientProfile(int clientId) { + if (DEBUG) { + Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")"); + } + + mClientProfiles.remove(clientId); + mListeners.remove(clientId); + mReleasedClientId.add(clientId); + } + + @Override + public boolean updateClientPriority(int clientId, int priority, int niceValue) { + if (DEBUG) { + Slog.d(TAG, "updateClientPriority(clientId=" + clientId + + ", priority=" + priority + ", niceValue=" + niceValue + ")"); + } + + ClientProfile profile = mClientProfiles.get(clientId); + if (profile == null) { + Slog.e(TAG, "Can not find client profile with id " + clientId + + " when trying to update the client priority."); + return false; + } + + profile.setPriority(priority); + profile.setNiceValue(niceValue); + + return true; + } + + @Override + public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) + throws RemoteException { + if (infos == null || infos.length == 0) { + Slog.d(TAG, "Can't update with empty frontend info"); + return; + } + + if (DEBUG) { + Slog.d(TAG, "updateFrontendInfo:"); + for (int i = 0; i < infos.length; i++) { + Slog.d(TAG, infos[i].toString()); + } + } + } + + @Override + public void updateCasInfo(int casSystemId, int maxSessionNum) { + if (DEBUG) { + Slog.d(TAG, "updateCasInfo(casSystemId=" + + casSystemId + ", maxSessionNum=" + maxSessionNum + ")"); + } + } + + @Override + public void setLnbInfoList(int[] lnbIds) { + if (DEBUG) { + for (int i = 0; i < lnbIds.length; i++) { + Slog.d(TAG, "updateLnbInfo(lnbId=" + lnbIds[i] + ")"); + } + } + } + + @Override + public boolean requestFrontend(@NonNull TunerFrontendRequest request, + @NonNull int[] frontendId) throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "requestFrontend(request=" + request + ")"); + } + + frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID; + + if (getContext() == null) { + Slog.e(TAG, "Can not find context when requesting frontend"); + return false; + } + + if (mClientProfiles.get(request.getClientId()) == null) { + Slog.e(TAG, "Request from unregistered client. Id: " + + request.getClientId()); + return false; + } + + String sessionId = mClientProfiles.get(request.getClientId()) + .getTvInputSessionId(); + + if (DEBUG) { + Slog.d(TAG, "session Id:" + sessionId + ")"); + } + + if (DEBUG) { + Slog.d(TAG, "No available Frontend found."); + } + + return false; + } + + @Override + public void shareFrontend(int selfClientId, int targetClientId) { + if (DEBUG) { + Slog.d(TAG, "shareFrontend from " + + selfClientId + " with " + targetClientId); + } + } + + @Override + public boolean requestCasSession(@NonNull CasSessionRequest request, + @NonNull int[] sessionResourceId) { + if (DEBUG) { + Slog.d(TAG, "requestCasSession(request=" + request + ")"); + } + + return true; + } + + @Override + public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbId) { + if (DEBUG) { + Slog.d(TAG, "requestLnb(request=" + request + ")"); + } + return true; + } + + @Override + public void releaseFrontend(int frontendId) { + if (DEBUG) { + Slog.d(TAG, "releaseFrontend(id=" + frontendId + ")"); + } + } + + @Override + public void releaseCasSession(int sessionResourceId) { + if (DEBUG) { + Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")"); + } + } + + @Override + public void releaseLnb(int lnbId) { + if (DEBUG) { + Slog.d(TAG, "releaseLnb(lnbId=" + lnbId + ")"); + } + } + + @Override + public boolean isHigherPriority(ResourceClientProfile challengerProfile, + ResourceClientProfile holderProfile) { + if (DEBUG) { + Slog.d(TAG, "isHigherPriority(challengerProfile=" + challengerProfile + + ", holderProfile=" + challengerProfile + ")"); + } + return true; + } + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index eef6c63d29c99..93662c91af90f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -162,6 +162,7 @@ import com.android.server.textservices.TextServicesManagerService; import com.android.server.trust.TrustManagerService; import com.android.server.tv.TvInputManagerService; import com.android.server.tv.TvRemoteService; +import com.android.server.tv.tuner.TunerResourceManagerService; import com.android.server.twilight.TwilightService; import com.android.server.uri.UriGrantsManagerService; import com.android.server.usage.UsageStatsService; @@ -1854,6 +1855,8 @@ public final class SystemServer { || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { t.traceBegin("StartTvInputManager"); mSystemServiceManager.startService(TvInputManagerService.class); + t.traceBegin("StartTunerResourceManager"); + mSystemServiceManager.startService(TunerResourceManagerService.class); t.traceEnd(); }