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