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