1 /* 2 * Copyright (C) 2015 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 com.android.tv.tuner.data; 18 19 import android.database.Cursor; 20 import android.support.annotation.NonNull; 21 import android.util.Log; 22 import com.android.tv.common.util.StringUtils; 23 import com.android.tv.tuner.data.Channel.DeliverySystemType; 24 import com.android.tv.tuner.data.Channel.TunerChannelProto; 25 import com.android.tv.tuner.data.Track.AtscAudioTrack; 26 import com.android.tv.tuner.data.Track.AtscCaptionTrack; 27 import java.io.IOException; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.List; 32 import java.util.Objects; 33 34 /** A class that represents a single channel accessible through a tuner. */ 35 public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracksInterface { 36 private static final String TAG = "TunerChannel"; 37 38 /** Channel number separator between major number and minor number. */ 39 public static final char CHANNEL_NUMBER_SEPARATOR = '-'; 40 41 // See ATSC Code Points Registry. 42 private static final String[] ATSC_SERVICE_TYPE_NAMES = 43 new String[] { 44 "ATSC Reserved", 45 "Analog television channels", 46 "ATSC_digital_television", 47 "ATSC_audio", 48 "ATSC_data_only_service", 49 "Software Download", 50 "Unassociated/Small Screen Service", 51 "Parameterized Service", 52 "ATSC NRT Service", 53 "Extended Parameterized Service" 54 }; 55 private static final String ATSC_SERVICE_TYPE_NAME_RESERVED = 56 ATSC_SERVICE_TYPE_NAMES[Channel.AtscServiceType.SERVICE_TYPE_ATSC_RESERVED_VALUE]; 57 58 public static final int INVALID_FREQUENCY = -1; 59 60 // According to RFC4259, The number of available PIDs ranges from 0 to 8191. 61 public static final int INVALID_PID = -1; 62 63 // According to ISO13818-1, Mpeg2 StreamType has a range from 0x00 to 0xff. 64 public static final int INVALID_STREAMTYPE = -1; 65 66 // @GuardedBy(this) Writing operations and toByteArray will be guarded. b/34197766 67 private TunerChannelProto mProto; 68 TunerChannel( PsipData.VctItem channel, int programNumber, List<PsiData.PmtItem> pmtItems, Channel.TunerType type)69 private TunerChannel( 70 PsipData.VctItem channel, 71 int programNumber, 72 List<PsiData.PmtItem> pmtItems, 73 Channel.TunerType type) { 74 String shortName = ""; 75 String longName = ""; 76 String description = ""; 77 int tsid = 0; 78 int virtualMajor = 0; 79 int virtualMinor = 0; 80 Channel.AtscServiceType serviceType = 81 Channel.AtscServiceType.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION; 82 if (channel != null) { 83 shortName = channel.getShortName(); 84 tsid = channel.getChannelTsid(); 85 programNumber = channel.getProgramNumber(); 86 virtualMajor = channel.getMajorChannelNumber(); 87 virtualMinor = channel.getMinorChannelNumber(); 88 Channel.AtscServiceType chanServiceType = 89 Channel.AtscServiceType.forNumber(channel.getServiceType()); 90 if (chanServiceType != null) { 91 serviceType = chanServiceType; 92 } 93 longName = (channel.getLongName() != null ? channel.getLongName() : longName); 94 description = 95 (channel.getDescription() != null ? channel.getDescription() : description); 96 } 97 TunerChannelProto tunerChannelProto = 98 TunerChannelProto.newBuilder() 99 .setShortName(shortName) 100 .setTsid(tsid) 101 .setProgramNumber(programNumber) 102 .setVirtualMajor(virtualMajor) 103 .setVirtualMinor(virtualMinor) 104 .setServiceType(serviceType) 105 .setLongName(longName) 106 .setDescription(description) 107 .build(); 108 initProto(pmtItems, type, tunerChannelProto); 109 } 110 initProto( List<PsiData.PmtItem> pmtItems, Channel.TunerType type, TunerChannelProto tunerChannelProto)111 private void initProto( 112 List<PsiData.PmtItem> pmtItems, 113 Channel.TunerType type, 114 TunerChannelProto tunerChannelProto) { 115 int videoPid = INVALID_PID; 116 int pcrPid = 0; 117 Channel.VideoStreamType videoStreamType = Channel.VideoStreamType.UNSET; 118 List<Integer> audioPids = new ArrayList<>(); 119 List<Channel.AudioStreamType> audioStreamTypes = new ArrayList<>(); 120 for (PsiData.PmtItem pmt : pmtItems) { 121 switch (pmt.getStreamType()) { 122 // MPEG ES stream video types 123 case Channel.VideoStreamType.MPEG1_VALUE: 124 case Channel.VideoStreamType.MPEG2_VALUE: 125 case Channel.VideoStreamType.H263_VALUE: 126 case Channel.VideoStreamType.H264_VALUE: 127 case Channel.VideoStreamType.H265_VALUE: 128 videoPid = pmt.getEsPid(); 129 videoStreamType = Channel.VideoStreamType.forNumber(pmt.getStreamType()); 130 break; 131 132 // MPEG ES stream audio types 133 case Channel.AudioStreamType.MPEG1AUDIO_VALUE: 134 case Channel.AudioStreamType.MPEG2AUDIO_VALUE: 135 case Channel.AudioStreamType.MPEG2AACAUDIO_VALUE: 136 case Channel.AudioStreamType.MPEG4LATMAACAUDIO_VALUE: 137 case Channel.AudioStreamType.A52AC3AUDIO_VALUE: 138 case Channel.AudioStreamType.EAC3AUDIO_VALUE: 139 audioPids.add(pmt.getEsPid()); 140 audioStreamTypes.add(Channel.AudioStreamType.forNumber(pmt.getStreamType())); 141 break; 142 143 // Non MPEG ES stream types 144 case 0x100: // PmtItem.ES_PID_PCR: 145 pcrPid = pmt.getEsPid(); 146 break; 147 default: 148 // fall out 149 } 150 } 151 mProto = 152 TunerChannelProto.newBuilder(tunerChannelProto) 153 .setType(type) 154 .setChannelId(-1L) 155 .setFrequency(INVALID_FREQUENCY) 156 .setVideoPid(videoPid) 157 .setVideoStreamType(videoStreamType) 158 .addAllAudioPids(audioPids) 159 .setAudioTrackIndex(audioPids.isEmpty() ? -1 : 0) 160 .addAllAudioStreamTypes(audioStreamTypes) 161 .setPcrPid(pcrPid) 162 .build(); 163 } 164 165 // b/141952456 may cause hasAudioTrackIndex and hasVideo to return false initChannelInfoIfNeeded()166 private void initChannelInfoIfNeeded() { 167 if (!mProto.hasAudioTrackIndex()) { 168 // Force select audio track 0 169 selectAudioTrack(0); 170 } 171 if (!mProto.hasVideoPid() && mProto.getVideoPid() != INVALID_PID) { 172 // Force set Video PID 173 setVideoPid(mProto.getVideoPid()); 174 } 175 } 176 TunerChannel( int programNumber, Channel.TunerType type, PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems)177 private TunerChannel( 178 int programNumber, 179 Channel.TunerType type, 180 PsipData.SdtItem channel, 181 List<PsiData.PmtItem> pmtItems) { 182 String shortName = ""; 183 Channel.AtscServiceType serviceType = 184 Channel.AtscServiceType.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION; 185 if (channel != null) { 186 shortName = channel.getServiceName(); 187 programNumber = channel.getServiceId(); 188 Channel.AtscServiceType chanServiceType = 189 Channel.AtscServiceType.forNumber(channel.getServiceType()); 190 if (chanServiceType != null) { 191 serviceType = chanServiceType; 192 } 193 } 194 TunerChannelProto tunerChannelProto = 195 TunerChannelProto.newBuilder() 196 .setShortName(shortName) 197 .setProgramNumber(programNumber) 198 .setServiceType(serviceType) 199 .build(); 200 initProto(pmtItems, type, tunerChannelProto); 201 } 202 203 /** Initialize tuner channel with VCT items and PMT items. */ TunerChannel(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems)204 public TunerChannel(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) { 205 this(channel, 0, pmtItems, Channel.TunerType.TYPE_TUNER); 206 } 207 208 /** Initialize tuner channel with program number and PMT items. */ TunerChannel(int programNumber, List<PsiData.PmtItem> pmtItems)209 public TunerChannel(int programNumber, List<PsiData.PmtItem> pmtItems) { 210 this(null, programNumber, pmtItems, Channel.TunerType.TYPE_TUNER); 211 } 212 213 /** Initialize tuner channel with SDT items and PMT items. */ TunerChannel(PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems)214 public TunerChannel(PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) { 215 this(0, Channel.TunerType.TYPE_TUNER, channel, pmtItems); 216 } 217 TunerChannel(TunerChannelProto tunerChannelProto)218 private TunerChannel(TunerChannelProto tunerChannelProto) { 219 mProto = tunerChannelProto; 220 initChannelInfoIfNeeded(); 221 } 222 forFile(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems)223 public static TunerChannel forFile(PsipData.VctItem channel, List<PsiData.PmtItem> pmtItems) { 224 return new TunerChannel(channel, 0, pmtItems, Channel.TunerType.TYPE_FILE); 225 } 226 forDvbFile( PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems)227 public static TunerChannel forDvbFile( 228 PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) { 229 return new TunerChannel(0, Channel.TunerType.TYPE_FILE, channel, pmtItems); 230 } 231 232 /** 233 * Create a TunerChannel object suitable for network tuners 234 * 235 * @param major Channel number major 236 * @param minor Channel number minor 237 * @param programNumber Program number 238 * @param shortName Short name 239 * @param recordingProhibited Recording prohibition info 240 * @param videoFormat Video format. Should be {@code null} or one of the followings: {@link 241 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_240P}, {@link 242 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_360P}, {@link 243 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_480I}, {@link 244 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_480P}, {@link 245 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_576I}, {@link 246 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_576P}, {@link 247 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_720P}, {@link 248 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080I}, {@link 249 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_1080P}, {@link 250 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_2160P}, {@link 251 * android.media.tv.TvContract.Channels#VIDEO_FORMAT_4320P} 252 * @return a TunerChannel object 253 */ forNetwork( int major, int minor, int programNumber, String shortName, boolean recordingProhibited, String videoFormat)254 public static TunerChannel forNetwork( 255 int major, 256 int minor, 257 int programNumber, 258 String shortName, 259 boolean recordingProhibited, 260 String videoFormat) { 261 TunerChannel tunerChannel = 262 new TunerChannel( 263 null, 264 programNumber, 265 Collections.EMPTY_LIST, 266 Channel.TunerType.TYPE_NETWORK); 267 tunerChannel.setVirtualMajor(major); 268 tunerChannel.setVirtualMinor(minor); 269 tunerChannel.setShortName(shortName); 270 // Set audio and video pids in order to work around the audio-only channel check. 271 tunerChannel.setAudioPids(new ArrayList<>(Arrays.asList(0))); 272 tunerChannel.selectAudioTrack(0); 273 tunerChannel.setVideoPid(0); 274 tunerChannel.setRecordingProhibited(recordingProhibited); 275 if (videoFormat != null) { 276 tunerChannel.setVideoFormat(videoFormat); 277 } 278 return tunerChannel; 279 } 280 getName()281 public String getName() { 282 return !mProto.getShortName().isEmpty() ? mProto.getShortName() : mProto.getLongName(); 283 } 284 getShortName()285 public String getShortName() { 286 return mProto.getShortName(); 287 } 288 getProgramNumber()289 public int getProgramNumber() { 290 return mProto.getProgramNumber(); 291 } 292 getServiceType()293 public int getServiceType() { 294 return mProto.getServiceType().getNumber(); 295 } 296 getServiceTypeName()297 public String getServiceTypeName() { 298 int serviceType = getServiceType(); 299 if (serviceType >= 0 && serviceType < ATSC_SERVICE_TYPE_NAMES.length) { 300 return ATSC_SERVICE_TYPE_NAMES[serviceType]; 301 } 302 return ATSC_SERVICE_TYPE_NAME_RESERVED; 303 } 304 getVirtualMajor()305 public int getVirtualMajor() { 306 return mProto.getVirtualMajor(); 307 } 308 getVirtualMinor()309 public int getVirtualMinor() { 310 return mProto.getVirtualMinor(); 311 } 312 getDeliverySystemType()313 public DeliverySystemType getDeliverySystemType() { 314 return mProto.getDeliverySystemType(); 315 } 316 getFrequency()317 public int getFrequency() { 318 return mProto.getFrequency(); 319 } 320 getModulation()321 public String getModulation() { 322 return mProto.getModulation(); 323 } 324 getTsid()325 public int getTsid() { 326 return mProto.getTsid(); 327 } 328 getVideoPid()329 public int getVideoPid() { 330 return mProto.getVideoPid(); 331 } 332 setVideoPid(int videoPid)333 public synchronized void setVideoPid(int videoPid) { 334 mProto = mProto.toBuilder().setVideoPid(videoPid).build(); 335 } 336 getVideoStreamType()337 public int getVideoStreamType() { 338 return mProto.getVideoStreamType().getNumber(); 339 } 340 getAudioPid()341 public int getAudioPid() { 342 if (!mProto.hasAudioTrackIndex() || mProto.getAudioTrackIndex() == -1) { 343 return INVALID_PID; 344 } 345 return mProto.getAudioPids(mProto.getAudioTrackIndex()); 346 } 347 getAudioStreamType()348 public int getAudioStreamType() { 349 if (!mProto.hasAudioTrackIndex() || mProto.getAudioTrackIndex() == -1) { 350 return INVALID_STREAMTYPE; 351 } 352 return mProto.getAudioStreamTypes(mProto.getAudioTrackIndex()).getNumber(); 353 } 354 getAudioPids()355 public List<Integer> getAudioPids() { 356 return mProto.getAudioPidsList(); 357 } 358 setAudioPids(List<Integer> audioPids)359 public synchronized void setAudioPids(List<Integer> audioPids) { 360 mProto = mProto.toBuilder().clearAudioPids().addAllAudioPids(audioPids).build(); 361 } 362 getAudioStreamTypes()363 public List<Integer> getAudioStreamTypes() { 364 List<Channel.AudioStreamType> audioStreamTypes = mProto.getAudioStreamTypesList(); 365 List<Integer> audioStreamTypesValues = new ArrayList<>(audioStreamTypes.size()); 366 367 for (Channel.AudioStreamType audioStreamType : audioStreamTypes) { 368 audioStreamTypesValues.add(audioStreamType.getNumber()); 369 } 370 return audioStreamTypesValues; 371 } 372 setAudioStreamTypes(List<Integer> audioStreamTypesValues)373 public synchronized void setAudioStreamTypes(List<Integer> audioStreamTypesValues) { 374 List<Channel.AudioStreamType> audioStreamTypes = 375 new ArrayList<>(audioStreamTypesValues.size()); 376 377 for (Integer audioStreamTypesValue : audioStreamTypesValues) { 378 audioStreamTypes.add(Channel.AudioStreamType.forNumber(audioStreamTypesValue)); 379 } 380 mProto = 381 mProto.toBuilder() 382 .clearAudioStreamTypes() 383 .addAllAudioStreamTypes(audioStreamTypes) 384 .build(); 385 } 386 getPcrPid()387 public int getPcrPid() { 388 return mProto.getPcrPid(); 389 } 390 getType()391 public Channel.TunerType getType() { 392 return mProto.getType(); 393 } 394 setFilepath(String filepath)395 public synchronized void setFilepath(String filepath) { 396 mProto = mProto.toBuilder().setFilepath(filepath == null ? "" : filepath).build(); 397 } 398 getFilepath()399 public String getFilepath() { 400 return mProto.getFilepath(); 401 } 402 setVirtualMajor(int virtualMajor)403 public synchronized void setVirtualMajor(int virtualMajor) { 404 mProto = mProto.toBuilder().setVirtualMajor(virtualMajor).build(); 405 } 406 setVirtualMinor(int virtualMinor)407 public synchronized void setVirtualMinor(int virtualMinor) { 408 mProto = mProto.toBuilder().setVirtualMinor(virtualMinor).build(); 409 } 410 setShortName(String shortName)411 public synchronized void setShortName(String shortName) { 412 mProto = mProto.toBuilder().setShortName(shortName == null ? "" : shortName).build(); 413 } 414 setDeliverySystemType(DeliverySystemType deliverySystemType)415 public synchronized void setDeliverySystemType(DeliverySystemType deliverySystemType) { 416 mProto = mProto.toBuilder().setDeliverySystemType(deliverySystemType).build(); 417 } 418 setFrequency(int frequency)419 public synchronized void setFrequency(int frequency) { 420 mProto = mProto.toBuilder().setFrequency(frequency).build(); 421 } 422 setModulation(String modulation)423 public synchronized void setModulation(String modulation) { 424 mProto = mProto.toBuilder().setModulation(modulation == null ? "" : modulation).build(); 425 } 426 hasVideo()427 public boolean hasVideo() { 428 return mProto.hasVideoPid() && mProto.getVideoPid() != INVALID_PID; 429 } 430 hasAudio()431 public boolean hasAudio() { 432 return getAudioPid() != INVALID_PID; 433 } 434 getChannelId()435 public long getChannelId() { 436 return mProto.getChannelId(); 437 } 438 setChannelId(long channelId)439 public synchronized void setChannelId(long channelId) { 440 mProto = mProto.toBuilder().setChannelId(channelId).build(); 441 } 442 443 /** 444 * The flag indicating whether this TV channel is locked or not. This is primarily used for 445 * alternative parental control to prevent unauthorized users from watching the current channel 446 * regardless of the content rating 447 * 448 * @see <a 449 * href="https://developer.android.com/reference/android/media/tv/TvContract.Channels.html#COLUMN_LOCKED">link</a> 450 */ isLocked()451 public boolean isLocked() { 452 return mProto.getLocked(); 453 } 454 setLocked(boolean locked)455 public synchronized void setLocked(boolean locked) { 456 mProto = mProto.toBuilder().setLocked(locked).build(); 457 } 458 getDisplayNumber()459 public String getDisplayNumber() { 460 return getDisplayNumber(true); 461 } 462 getDisplayNumber(boolean ignoreZeroMinorNumber)463 public String getDisplayNumber(boolean ignoreZeroMinorNumber) { 464 if (getVirtualMajor() != 0 && (getVirtualMinor() != 0 || !ignoreZeroMinorNumber)) { 465 return String.format( 466 "%d%c%d", getVirtualMajor(), CHANNEL_NUMBER_SEPARATOR, getVirtualMinor()); 467 } else if (getVirtualMajor() != 0) { 468 return Integer.toString(getVirtualMajor()); 469 } else { 470 return Integer.toString(getProgramNumber()); 471 } 472 } 473 getDescription()474 public String getDescription() { 475 return mProto.getDescription(); 476 } 477 478 @Override setHasCaptionTrack()479 public synchronized void setHasCaptionTrack() { 480 mProto = mProto.toBuilder().setHasCaptionTrack(true).build(); 481 } 482 483 @Override hasCaptionTrack()484 public boolean hasCaptionTrack() { 485 return mProto.getHasCaptionTrack(); 486 } 487 488 @Override getAudioTracks()489 public List<AtscAudioTrack> getAudioTracks() { 490 return mProto.getAudioTracksList(); 491 } 492 setAudioTracks(List<AtscAudioTrack> audioTracks)493 public synchronized void setAudioTracks(List<AtscAudioTrack> audioTracks) { 494 mProto = mProto.toBuilder().clearAudioTracks().addAllAudioTracks(audioTracks).build(); 495 } 496 497 @Override getCaptionTracks()498 public List<AtscCaptionTrack> getCaptionTracks() { 499 return mProto.getCaptionTracksList(); 500 } 501 setCaptionTracks(List<AtscCaptionTrack> captionTracks)502 public synchronized void setCaptionTracks(List<AtscCaptionTrack> captionTracks) { 503 mProto = mProto.toBuilder().clearCaptionTracks().addAllCaptionTracks(captionTracks).build(); 504 } 505 selectAudioTrack(int index)506 public synchronized void selectAudioTrack(int index) { 507 if (index < 0 || index >= mProto.getAudioPidsCount()) { 508 index = -1; 509 } 510 mProto = mProto.toBuilder().setAudioTrackIndex(index).build(); 511 } 512 setRecordingProhibited(boolean recordingProhibited)513 public synchronized void setRecordingProhibited(boolean recordingProhibited) { 514 mProto = mProto.toBuilder().setRecordingProhibited(recordingProhibited).build(); 515 } 516 isRecordingProhibited()517 public boolean isRecordingProhibited() { 518 return mProto.getRecordingProhibited(); 519 } 520 setVideoFormat(String videoFormat)521 public synchronized void setVideoFormat(String videoFormat) { 522 mProto = mProto.toBuilder().setVideoFormat(videoFormat == null ? "" : videoFormat).build(); 523 } 524 getVideoFormat()525 public String getVideoFormat() { 526 return mProto.getVideoFormat(); 527 } 528 529 @Override toString()530 public String toString() { 531 switch (getType()) { 532 case TYPE_FILE: 533 return String.format( 534 "{%d-%d %s} Filepath: %s, ProgramNumber %d", 535 getVirtualMajor(), 536 getVirtualMinor(), 537 getShortName(), 538 getFilepath(), 539 getProgramNumber()); 540 // case Channel.TunerType.TYPE_TUNER: 541 default: 542 return String.format( 543 "{%d-%d %s} Frequency: %d, ProgramNumber %d", 544 getVirtualMajor(), 545 getVirtualMinor(), 546 getShortName(), 547 getFrequency(), 548 getProgramNumber()); 549 } 550 } 551 552 @Override compareTo(@onNull TunerChannel channel)553 public int compareTo(@NonNull TunerChannel channel) { 554 // In the same frequency, the program number acts as the sub-channel number. 555 int ret = getFrequency() - channel.getFrequency(); 556 if (ret != 0) { 557 return ret; 558 } 559 ret = getProgramNumber() - channel.getProgramNumber(); 560 if (ret != 0) { 561 return ret; 562 } 563 ret = StringUtils.compare(getName(), channel.getName()); 564 if (ret != 0) { 565 return ret; 566 } 567 if (getDeliverySystemType() != channel.getDeliverySystemType()) { 568 return 1; 569 } 570 // For FileTsStreamer, file paths should be compared. 571 return StringUtils.compare(getFilepath(), channel.getFilepath()); 572 } 573 574 @Override equals(Object o)575 public boolean equals(Object o) { 576 if (!(o instanceof TunerChannel)) { 577 return false; 578 } 579 return compareTo((TunerChannel) o) == 0; 580 } 581 582 @Override hashCode()583 public int hashCode() { 584 return Objects.hash(getDeliverySystemType(), getFrequency(), getProgramNumber(), getName(), 585 getFilepath()); 586 } 587 588 // Serialization toByteArray()589 public synchronized byte[] toByteArray() { 590 try { 591 return mProto.toByteArray(); 592 } catch (Exception e) { 593 // Retry toByteArray. b/34197766 594 Log.w( 595 TAG, 596 "TunerChannel or its variables are modified in multiple thread without lock", 597 e); 598 return mProto.toByteArray(); 599 } 600 } 601 parseFrom(byte[] data)602 public static TunerChannel parseFrom(byte[] data) { 603 if (data == null) { 604 return null; 605 } 606 try { 607 return new TunerChannel(TunerChannelProto.parseFrom(data)); 608 } catch (IOException e) { 609 Log.e(TAG, "Could not parse from byte array", e); 610 return null; 611 } 612 } 613 fromCursor(Cursor cursor)614 public static TunerChannel fromCursor(Cursor cursor) { 615 long channelId = cursor.getLong(0); 616 boolean locked = cursor.getInt(1) > 0; 617 byte[] data = cursor.getBlob(2); 618 TunerChannel channel = TunerChannel.parseFrom(data); 619 if (channel != null) { 620 channel.setChannelId(channelId); 621 channel.setLocked(locked); 622 } 623 return channel; 624 } 625 } 626