1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media.tv;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.StringDef;
22 import android.os.Parcel;
23 import android.os.ParcelFileDescriptor;
24 import android.os.Parcelable;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.util.ArrayList;
29 import java.util.List;
30 
31 /**
32  * A response for DSM-CC from broadcast signal.
33  */
34 public final class DsmccResponse extends BroadcastInfoResponse implements Parcelable {
35     private static final @TvInputManager.BroadcastInfoType int RESPONSE_TYPE =
36             TvInputManager.BROADCAST_INFO_TYPE_DSMCC;
37 
38     /** @hide */
39     @Retention(RetentionPolicy.SOURCE)
40     @StringDef(prefix = "BIOP_MESSAGE_TYPE_", value = {
41             BIOP_MESSAGE_TYPE_DIRECTORY,
42             BIOP_MESSAGE_TYPE_FILE,
43             BIOP_MESSAGE_TYPE_STREAM,
44             BIOP_MESSAGE_TYPE_SERVICE_GATEWAY,
45 
46     })
47     public @interface BiopMessageType {}
48 
49     /** Broadcast Inter-ORB Protocol (BIOP) message types */
50     /** BIOP directory message */
51     public static final String BIOP_MESSAGE_TYPE_DIRECTORY = "directory";
52     /** BIOP file message */
53     public static final String BIOP_MESSAGE_TYPE_FILE = "file";
54     /** BIOP stream message */
55     public static final String BIOP_MESSAGE_TYPE_STREAM = "stream";
56     /** BIOP service gateway message */
57     public static final String BIOP_MESSAGE_TYPE_SERVICE_GATEWAY = "service_gateway";
58 
59     public static final @NonNull Parcelable.Creator<DsmccResponse> CREATOR =
60             new Parcelable.Creator<DsmccResponse>() {
61                 @Override
62                 public DsmccResponse createFromParcel(Parcel source) {
63                     source.readInt();
64                     return createFromParcelBody(source);
65                 }
66 
67                 @Override
68                 public DsmccResponse[] newArray(int size) {
69                     return new DsmccResponse[size];
70                 }
71             };
72 
73     private final @BiopMessageType String mBiopMessageType;
74     private final ParcelFileDescriptor mFileDescriptor;
75     private final List<String> mChildList;
76     private final int[] mEventIds;
77     private final String[] mEventNames;
78 
createFromParcelBody(Parcel in)79     static DsmccResponse createFromParcelBody(Parcel in) {
80         return new DsmccResponse(in);
81     }
82 
83     /**
84      * Constructs a BIOP file message response.
85      */
DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult, @Nullable ParcelFileDescriptor file)86     public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
87             @Nullable ParcelFileDescriptor file) {
88         super(RESPONSE_TYPE, requestId, sequence, responseResult);
89         mBiopMessageType = BIOP_MESSAGE_TYPE_FILE;
90         mFileDescriptor = file;
91         mChildList = null;
92         mEventIds = null;
93         mEventNames = null;
94     }
95 
96     /**
97      * Constructs a BIOP service gateway or directory message response.
98      */
DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult, boolean isServiceGateway, @Nullable List<String> childList)99     public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
100             boolean isServiceGateway, @Nullable List<String> childList) {
101         super(RESPONSE_TYPE, requestId, sequence, responseResult);
102         if (isServiceGateway) {
103             mBiopMessageType = BIOP_MESSAGE_TYPE_SERVICE_GATEWAY;
104         } else {
105             mBiopMessageType = BIOP_MESSAGE_TYPE_DIRECTORY;
106         }
107         mFileDescriptor = null;
108         mChildList = childList;
109         mEventIds = null;
110         mEventNames = null;
111     }
112 
113     /**
114      * Constructs a BIOP stream message response.
115      *
116      * <p>The current stream message response does not support other stream messages types than
117      * stream event message type.
118      */
DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult, @Nullable int[] eventIds, @Nullable String[] eventNames)119     public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
120             @Nullable int[] eventIds, @Nullable String[] eventNames) {
121         super(RESPONSE_TYPE, requestId, sequence, responseResult);
122         mBiopMessageType = BIOP_MESSAGE_TYPE_STREAM;
123         mFileDescriptor = null;
124         mChildList = null;
125         if (!((eventIds != null && eventNames != null && eventIds.length == eventNames.length)
126                 || (eventIds == null && eventNames == null))) {
127             throw new IllegalStateException("The size of eventIds and eventNames must be equal");
128         }
129         mEventIds = eventIds;
130         mEventNames = eventNames;
131     }
132 
DsmccResponse(@onNull Parcel source)133     private DsmccResponse(@NonNull Parcel source) {
134         super(RESPONSE_TYPE, source);
135 
136         mBiopMessageType = source.readString();
137         switch (mBiopMessageType) {
138             case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
139             case BIOP_MESSAGE_TYPE_DIRECTORY:
140                 int childNum = source.readInt();
141                 if (childNum > 0) {
142                     mChildList = new ArrayList<>();
143                     for (int i = 0; i < childNum; i++) {
144                         mChildList.add(source.readString());
145                     }
146                 } else
147                     mChildList = null;
148                 mFileDescriptor = null;
149                 mEventIds = null;
150                 mEventNames = null;
151                 break;
152             case BIOP_MESSAGE_TYPE_FILE:
153                 mFileDescriptor = source.readFileDescriptor();
154                 mChildList = null;
155                 mEventIds = null;
156                 mEventNames = null;
157                 break;
158             case BIOP_MESSAGE_TYPE_STREAM:
159                 int eventNum = source.readInt();
160                 if (eventNum > 0) {
161                     mEventIds = new int[eventNum];
162                     mEventNames = new String[eventNum];
163                     for (int i = 0; i < eventNum; i++) {
164                         mEventIds[i] = source.readInt();
165                         mEventNames[i] = source.readString();
166                     }
167                 } else {
168                     mEventIds = null;
169                     mEventNames = null;
170                 }
171                 mChildList = null;
172                 mFileDescriptor = null;
173                 break;
174             default:
175                 throw new IllegalStateException("unexpected BIOP message type");
176         }
177     }
178 
179     /**
180      * Returns the BIOP message type.
181      */
182     @NonNull
getBiopMessageType()183     public @BiopMessageType String getBiopMessageType() {
184         return mBiopMessageType;
185     }
186 
187     /**
188      * Returns the file descriptor for a given file message response.
189      */
190     @NonNull
getFile()191     public ParcelFileDescriptor getFile() {
192         if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_FILE)) {
193             throw new IllegalStateException("Not file object");
194         }
195         return mFileDescriptor;
196     }
197 
198     /**
199      * Returns a list of subobject names for the given service gateway or directory message
200      * response.
201      */
202     @NonNull
getChildList()203     public List<String> getChildList() {
204         if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_DIRECTORY)
205                 && !mBiopMessageType.equals(BIOP_MESSAGE_TYPE_SERVICE_GATEWAY)) {
206             throw new IllegalStateException("Not directory object");
207         }
208         return mChildList != null ? new ArrayList<String>(mChildList) : new ArrayList<String>();
209     }
210 
211     /**
212      * Returns all event IDs carried in a given stream message response.
213      */
214     @NonNull
getStreamEventIds()215     public int[] getStreamEventIds() {
216         if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
217             throw new IllegalStateException("Not stream event object");
218         }
219         return mEventIds != null ? mEventIds : new int[0];
220     }
221 
222     /**
223      * Returns all event names carried in a given stream message response.
224      */
225     @NonNull
getStreamEventNames()226     public String[] getStreamEventNames() {
227         if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
228             throw new IllegalStateException("Not stream event object");
229         }
230         return mEventNames != null ? mEventNames : new String[0];
231     }
232 
233     @Override
describeContents()234     public int describeContents() {
235         return 0;
236     }
237 
238     @Override
writeToParcel(@onNull Parcel dest, int flags)239     public void writeToParcel(@NonNull Parcel dest, int flags) {
240         super.writeToParcel(dest, flags);
241         dest.writeString(mBiopMessageType);
242         switch (mBiopMessageType) {
243             case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
244             case BIOP_MESSAGE_TYPE_DIRECTORY:
245                 if (mChildList != null && mChildList.size() > 0) {
246                     dest.writeInt(mChildList.size());
247                     for (String child : mChildList) {
248                         dest.writeString(child);
249                     }
250                 } else
251                     dest.writeInt(0);
252                 break;
253             case BIOP_MESSAGE_TYPE_FILE:
254                 dest.writeFileDescriptor(mFileDescriptor.getFileDescriptor());
255                 break;
256             case BIOP_MESSAGE_TYPE_STREAM:
257                 if (mEventIds != null && mEventIds.length > 0) {
258                     dest.writeInt(mEventIds.length);
259                     for (int i = 0; i < mEventIds.length; i++) {
260                         dest.writeInt(mEventIds[i]);
261                         dest.writeString(mEventNames[i]);
262                     }
263                 } else
264                     dest.writeInt(0);
265                 break;
266             default:
267                 throw new IllegalStateException("unexpected BIOP message type");
268         }
269     }
270 }
271