1 /* 2 * Copyright (C) 2014 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.cts; 18 19 import android.content.ContentResolver; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.database.Cursor; 23 import android.graphics.Canvas; 24 import android.graphics.Color; 25 import android.media.AudioPresentation; 26 import android.media.tv.TvContract; 27 import android.media.tv.TvInputInfo; 28 import android.media.tv.TvInputManager; 29 import android.media.tv.TvInputService; 30 import android.media.tv.TvTrackInfo; 31 import android.net.Uri; 32 import android.view.Surface; 33 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.List; 37 38 public class StubTunerTvInputService extends TvInputService { 39 public static final AudioPresentation TEST_AUDIO_PRESENTATION = 40 new AudioPresentation.Builder(1).setProgramId(123).build(); 41 42 private static final List<TvTrackInfo> mTrackList = new ArrayList<>(); 43 clearTracks()44 public static void clearTracks() { 45 mTrackList.clear(); 46 } 47 injectTrack(TvTrackInfo... tracks)48 public static void injectTrack(TvTrackInfo... tracks) { 49 mTrackList.addAll(Arrays.asList(tracks)); 50 } 51 insertChannels(ContentResolver resolver, TvInputInfo info)52 public static void insertChannels(ContentResolver resolver, TvInputInfo info) { 53 if (!info.getServiceInfo().name.equals(StubTunerTvInputService.class.getName())) { 54 throw new IllegalArgumentException("info mismatch"); 55 } 56 ContentValues redValues = new ContentValues(); 57 redValues.put(TvContract.Channels.COLUMN_INPUT_ID, info.getId()); 58 redValues.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "0"); 59 redValues.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "Red"); 60 redValues.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, new byte[] { 0 }); 61 ContentValues greenValues = new ContentValues(); 62 greenValues.put(TvContract.Channels.COLUMN_INPUT_ID, info.getId()); 63 greenValues.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "1"); 64 greenValues.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "Green"); 65 greenValues.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, new byte[] { 1 }); 66 ContentValues blueValues = new ContentValues(); 67 blueValues.put(TvContract.Channels.COLUMN_INPUT_ID, info.getId()); 68 blueValues.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "2"); 69 blueValues.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "Blue"); 70 blueValues.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, new byte[] { 2 }); 71 resolver.bulkInsert(TvContract.Channels.CONTENT_URI, 72 new ContentValues[] { redValues, greenValues, blueValues }); 73 } 74 deleteChannels(ContentResolver resolver, TvInputInfo info)75 public static void deleteChannels(ContentResolver resolver, TvInputInfo info) { 76 if (!info.getServiceInfo().name.equals(StubTunerTvInputService.class.getName())) { 77 throw new IllegalArgumentException("info mismatch"); 78 } 79 resolver.delete(TvContract.buildChannelsUriForInput(info.getId()), null, null); 80 } 81 82 @Override onCreateSession(String inputId)83 public Session onCreateSession(String inputId) { 84 return new StubSessionImpl(this); 85 } 86 87 static class StubSessionImpl extends Session { 88 private static final int[] COLORS = { Color.RED, Color.GREEN, Color.BLUE }; 89 private Surface mSurface; 90 private Object mLock = new Object(); 91 private int mCurrentIndex = -1; 92 private Context mContext; 93 StubSessionImpl(Context context)94 StubSessionImpl(Context context) { 95 super(context); 96 mContext = context; 97 } 98 99 @Override onRelease()100 public void onRelease() { 101 } 102 updateSurfaceLocked()103 private void updateSurfaceLocked() { 104 if (mCurrentIndex >= 0 && mSurface != null) { 105 try { 106 Canvas c = mSurface.lockCanvas(null); 107 c.drawColor(COLORS[mCurrentIndex]); 108 mSurface.unlockCanvasAndPost(c); 109 } catch (IllegalArgumentException e) { 110 mSurface = null; 111 } 112 } 113 } 114 115 @Override onSetSurface(Surface surface)116 public boolean onSetSurface(Surface surface) { 117 synchronized (mLock) { 118 mSurface = surface; 119 updateSurfaceLocked(); 120 return true; 121 } 122 } 123 124 @Override onSetStreamVolume(float volume)125 public void onSetStreamVolume(float volume) { 126 } 127 128 @Override onTune(Uri channelUri)129 public boolean onTune(Uri channelUri) { 130 notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING); 131 synchronized (mLock) { 132 String[] projection = { TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA }; 133 Cursor cursor = mContext.getContentResolver().query( 134 channelUri, projection, null, null, null); 135 try { 136 if (cursor != null && cursor.moveToNext()) { 137 mCurrentIndex = cursor.getBlob(0)[0]; 138 } else { 139 mCurrentIndex = -1; 140 } 141 } finally { 142 if (cursor != null) { 143 cursor.close(); 144 } 145 } 146 updateSurfaceLocked(); 147 // Notify tracks 148 if (mCurrentIndex == 0) { 149 notifyTracksChanged(mTrackList); 150 for (TvTrackInfo track : mTrackList) { 151 if (track.getType() == TvTrackInfo.TYPE_VIDEO) { 152 notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, track.getId()); 153 break; 154 } 155 } 156 for (TvTrackInfo track : mTrackList) { 157 if (track.getType() == TvTrackInfo.TYPE_AUDIO) { 158 notifyTrackSelected(TvTrackInfo.TYPE_AUDIO, track.getId()); 159 break; 160 } 161 } 162 } 163 } 164 notifyVideoAvailable(); 165 notifyAudioPresentationChanged(Arrays.asList(TEST_AUDIO_PRESENTATION)); 166 return true; 167 } 168 169 @Override onSelectTrack(int type, String trackId)170 public boolean onSelectTrack(int type, String trackId) { 171 notifyTrackSelected(type, trackId); 172 return true; 173 } 174 175 @Override onSetCaptionEnabled(boolean enabled)176 public void onSetCaptionEnabled(boolean enabled) { 177 } 178 } 179 } 180