| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.media.tv.cts; |
| |
| import android.app.Activity; |
| import android.app.ActivityManager; |
| import android.app.ActivityManager.RunningAppProcessInfo; |
| import android.app.Instrumentation; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.database.Cursor; |
| import android.media.AudioDeviceInfo; |
| import android.media.AudioFormat; |
| import android.media.AudioManager; |
| import android.media.tv.cts.TvViewTest.MockCallback; |
| import android.media.tv.TunedInfo; |
| import android.media.tv.TvContentRating; |
| import android.media.tv.TvContract; |
| import android.media.tv.TvInputHardwareInfo; |
| import android.media.tv.TvInputInfo; |
| import android.media.tv.TvInputManager; |
| import android.media.tv.TvInputManager.Hardware; |
| import android.media.tv.TvInputManager.HardwareCallback; |
| import android.media.tv.TvInputManager.Session; |
| import android.media.tv.TvInputManager.SessionCallback; |
| import android.media.tv.TvInputService; |
| import android.media.tv.TvStreamConfig; |
| import android.media.tv.TvView; |
| import android.media.tv.tunerresourcemanager.TunerResourceManager; |
| import android.net.Uri; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Looper; |
| import android.test.ActivityInstrumentationTestCase2; |
| import android.tv.cts.R; |
| |
| import com.android.compatibility.common.util.PollingCheck; |
| |
| import androidx.test.InstrumentationRegistry; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.Executor; |
| |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| /** |
| * Test for {@link android.media.tv.TvInputManager}. |
| */ |
| public class TvInputManagerTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> { |
| /** The maximum time to wait for an operation. */ |
| private static final long TIME_OUT_MS = 15000L; |
| private static final int PRIORITY_HINT_USE_CASE_TYPE_INVALID = 1000; |
| |
| private static final int DUMMY_DEVICE_ID = Integer.MAX_VALUE; |
| private static final String[] VALID_TV_INPUT_SERVICES = { |
| StubTunerTvInputService.class.getName() |
| }; |
| private static final String[] INVALID_TV_INPUT_SERVICES = { |
| NoMetadataTvInputService.class.getName(), NoPermissionTvInputService.class.getName() |
| }; |
| private static final String EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION = |
| "android.media.tv.cts.TvInputManagerTest.EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION"; |
| private static final String EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED = |
| "android.media.tv.cts.TvInputManagerTest" |
| + ".EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED"; |
| private static final String EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED = |
| "android.media.tv.cts.TvInputManagerTest" |
| + ".EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED"; |
| private static final String PERMISSION_GRANTED = |
| "android.media.tv.cts.TvInputManagerTest.PERMISSION_GRANTED"; |
| private static final String PERMISSION_UNGRANTED = |
| "android.media.tv.cts.TvInputManagerTest.PERMISSION_UNGRANTED"; |
| |
| private static final TvContentRating DUMMY_RATING = TvContentRating.createRating( |
| "com.android.tv", "US_TV", "US_TV_PG", "US_TV_D", "US_TV_L"); |
| |
| private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS = |
| "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"; |
| private static final String PERMISSION_WRITE_EPG_DATA = |
| "com.android.providers.tv.permission.WRITE_EPG_DATA"; |
| private static final String PERMISSION_ACCESS_TUNED_INFO = |
| "android.permission.ACCESS_TUNED_INFO"; |
| private static final String PERMISSION_TV_INPUT_HARDWARE = |
| "android.permission.TV_INPUT_HARDWARE"; |
| private static final String PERMISSION_TUNER_RESOURCE_ACCESS = |
| "android.permission.TUNER_RESOURCE_ACCESS"; |
| private static final String PERMISSION_TIS_EXTENSION_INTERFACE = |
| "android.permission.TIS_EXTENSION_INTERFACE"; |
| private static final String[] BASE_SHELL_PERMISSIONS = { |
| PERMISSION_ACCESS_WATCHED_PROGRAMS, |
| PERMISSION_WRITE_EPG_DATA, |
| PERMISSION_ACCESS_TUNED_INFO, |
| PERMISSION_TUNER_RESOURCE_ACCESS, |
| PERMISSION_TIS_EXTENSION_INTERFACE |
| }; |
| |
| private String mStubId; |
| private TvInputManager mManager; |
| private LoggingCallback mCallback = new LoggingCallback(); |
| private TvInputInfo mStubTvInputInfo; |
| private TvView mTvView; |
| private Activity mActivity; |
| private Instrumentation mInstrumentation; |
| private TvInputInfo mStubTunerTvInputInfo; |
| private final MockCallback mMockCallback = new MockCallback(); |
| |
| private static TvInputInfo getInfoForClassName(List<TvInputInfo> list, String name) { |
| for (TvInputInfo info : list) { |
| if (info.getServiceInfo().name.equals(name)) { |
| return info; |
| } |
| } |
| return null; |
| } |
| |
| private static boolean isHardwareDeviceAdded(List<TvInputHardwareInfo> list, int deviceId) { |
| if (list != null) { |
| for (TvInputHardwareInfo info : list) { |
| if (info.getDeviceId() == deviceId) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private String prepareStubHardwareTvInputService() { |
| String[] newPermissions = Arrays.copyOf( |
| BASE_SHELL_PERMISSIONS, BASE_SHELL_PERMISSIONS.length + 1); |
| newPermissions[BASE_SHELL_PERMISSIONS.length] = PERMISSION_TV_INPUT_HARDWARE; |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(newPermissions); |
| |
| // Use the test api to add an HDMI hardware device |
| mManager.addHardwareDevice(DUMMY_DEVICE_ID); |
| assertTrue(isHardwareDeviceAdded(mManager.getHardwareList(), DUMMY_DEVICE_ID)); |
| |
| PackageManager pm = getActivity().getPackageManager(); |
| ComponentName component = |
| new ComponentName(getActivity(), StubHardwareTvInputService.class); |
| pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, |
| PackageManager.DONT_KILL_APP); |
| new PollingCheck(TIME_OUT_MS) { |
| @Override |
| protected boolean check() { |
| return null != getInfoForClassName( |
| mManager.getTvInputList(), StubHardwareTvInputService.class.getName()); |
| } |
| }.run(); |
| |
| TvInputInfo info = getInfoForClassName( |
| mManager.getTvInputList(), StubHardwareTvInputService.class.getName()); |
| assertNotNull(info); |
| return info.getId(); |
| } |
| |
| private void cleanupStubHardwareTvInputService() { |
| // Restore the base shell permissions |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(BASE_SHELL_PERMISSIONS); |
| |
| PackageManager pm = getActivity().getPackageManager(); |
| ComponentName component = |
| new ComponentName(getActivity(), StubHardwareTvInputService.class); |
| pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, |
| PackageManager.DONT_KILL_APP); |
| new PollingCheck(TIME_OUT_MS) { |
| @Override |
| protected boolean check() { |
| return null == getInfoForClassName( |
| mManager.getTvInputList(), StubHardwareTvInputService.class.getName()); |
| } |
| }.run(); |
| |
| mManager.removeHardwareDevice(DUMMY_DEVICE_ID); |
| } |
| |
| public TvInputManagerTest() { |
| super(TvViewStubActivity.class); |
| } |
| |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| mActivity = getActivity(); |
| if (!Utils.hasTvInputFramework(mActivity)) { |
| return; |
| } |
| |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(BASE_SHELL_PERMISSIONS); |
| |
| mInstrumentation = getInstrumentation(); |
| mTvView = findTvViewById(R.id.tvview); |
| mManager = (TvInputManager) mActivity.getSystemService(Context.TV_INPUT_SERVICE); |
| mStubId = getInfoForClassName( |
| mManager.getTvInputList(), StubTvInputService2.class.getName()).getId(); |
| mStubTvInputInfo = getInfoForClassName( |
| mManager.getTvInputList(), StubTvInputService2.class.getName()); |
| for (TvInputInfo info : mManager.getTvInputList()) { |
| if (info.getServiceInfo().name.equals(StubTunerTvInputService.class.getName())) { |
| mStubTunerTvInputInfo = info; |
| break; |
| } |
| } |
| assertNotNull(mStubTunerTvInputInfo); |
| mTvView.setCallback(mMockCallback); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| super.tearDown(); |
| return; |
| } |
| StubTunerTvInputService.deleteChannels( |
| mActivity.getContentResolver(), mStubTunerTvInputInfo); |
| StubTunerTvInputService.clearTracks(); |
| try { |
| runTestOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| mTvView.reset(); |
| } |
| }); |
| } catch (Throwable t) { |
| throw new RuntimeException(t); |
| } |
| mInstrumentation.waitForIdleSync(); |
| |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| super.tearDown(); |
| } |
| |
| private TvView findTvViewById(int id) { |
| return (TvView) mActivity.findViewById(id); |
| } |
| |
| private void tryTuneAllChannels() throws Throwable { |
| StubTunerTvInputService.insertChannels( |
| mActivity.getContentResolver(), mStubTunerTvInputInfo); |
| |
| Uri uri = TvContract.buildChannelsUriForInput(mStubTunerTvInputInfo.getId()); |
| String[] projection = { TvContract.Channels._ID }; |
| try (Cursor cursor = mActivity.getContentResolver().query( |
| uri, projection, null, null, null)) { |
| while (cursor != null && cursor.moveToNext()) { |
| long channelId = cursor.getLong(0); |
| Uri channelUri = TvContract.buildChannelUri(channelId); |
| mCallback.mTunedInfos = null; |
| mTvView.tune(mStubTunerTvInputInfo.getId(), channelUri); |
| mInstrumentation.waitForIdleSync(); |
| new PollingCheck(TIME_OUT_MS) { |
| @Override |
| protected boolean check() { |
| return mMockCallback.isVideoAvailable(mStubTunerTvInputInfo.getId()); |
| } |
| }.run(); |
| new PollingCheck(TIME_OUT_MS) { |
| @Override |
| protected boolean check() { |
| return mCallback.mTunedInfos != null; |
| } |
| }.run(); |
| |
| List<TunedInfo> returnedInfos = mManager.getCurrentTunedInfos(); |
| assertEquals(1, returnedInfos.size()); |
| TunedInfo returnedInfo = returnedInfos.get(0); |
| TunedInfo expectedInfo = new TunedInfo( |
| "android.tv.cts/android.media.tv.cts.StubTunerTvInputService", |
| channelUri, |
| false, |
| false, |
| false, |
| TunedInfo.APP_TYPE_SELF, |
| TunedInfo.APP_TAG_SELF); |
| assertEquals(expectedInfo, returnedInfo); |
| |
| assertEquals(expectedInfo.getAppTag(), returnedInfo.getAppTag()); |
| assertEquals(expectedInfo.getAppType(), returnedInfo.getAppType()); |
| assertEquals(expectedInfo.getChannelUri(), returnedInfo.getChannelUri()); |
| assertEquals(expectedInfo.getInputId(), returnedInfo.getInputId()); |
| assertEquals(expectedInfo.isMainSession(), returnedInfo.isMainSession()); |
| assertEquals(expectedInfo.isRecordingSession(), returnedInfo.isRecordingSession()); |
| assertEquals(expectedInfo.isVisible(), returnedInfo.isVisible()); |
| |
| assertEquals(1, mCallback.mTunedInfos.size()); |
| TunedInfo callbackInfo = mCallback.mTunedInfos.get(0); |
| assertEquals(expectedInfo, callbackInfo); |
| } |
| } |
| } |
| |
| public void testGetCurrentTunedInfos() throws Throwable { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| mActivity.runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| mManager.registerCallback(mCallback, new Handler()); |
| } |
| }); |
| tryTuneAllChannels(); |
| mActivity.runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| mManager.unregisterCallback(mCallback); |
| } |
| }); |
| } |
| |
| public void testGetInputState() throws Exception { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| assertEquals(mManager.getInputState(mStubId), TvInputManager.INPUT_STATE_CONNECTED); |
| } |
| |
| public void testGetTvInputInfo() throws Exception { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| TvInputInfo expected = mManager.getTvInputInfo(mStubId); |
| TvInputInfo actual = getInfoForClassName(mManager.getTvInputList(), |
| StubTvInputService2.class.getName()); |
| assertTrue("expected=" + expected + " actual=" + actual, |
| TvInputInfoTest.compareTvInputInfos(getActivity(), expected, actual)); |
| } |
| |
| public void testGetTvInputList() throws Exception { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| List<TvInputInfo> list = mManager.getTvInputList(); |
| for (String name : VALID_TV_INPUT_SERVICES) { |
| assertNotNull("getTvInputList() doesn't contain valid input: " + name, |
| getInfoForClassName(list, name)); |
| } |
| for (String name : INVALID_TV_INPUT_SERVICES) { |
| assertNull("getTvInputList() contains invalind input: " + name, |
| getInfoForClassName(list, name)); |
| } |
| } |
| |
| public void testIsParentalControlsEnabled() { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| try { |
| mManager.isParentalControlsEnabled(); |
| } catch (Exception e) { |
| fail(); |
| } |
| } |
| |
| public void testIsRatingBlocked() { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| try { |
| mManager.isRatingBlocked(DUMMY_RATING); |
| } catch (Exception e) { |
| fail(); |
| } |
| } |
| |
| public void testRegisterUnregisterCallback() { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| getActivity().runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| mManager.registerCallback(mCallback, new Handler()); |
| mManager.unregisterCallback(mCallback); |
| } catch (Exception e) { |
| fail(); |
| } |
| } |
| }); |
| getInstrumentation().waitForIdleSync(); |
| } |
| |
| public void testInputAddedAndRemoved() { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| getActivity().runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| mManager.registerCallback(mCallback, new Handler()); |
| } |
| }); |
| getInstrumentation().waitForIdleSync(); |
| |
| // Test if onInputRemoved() is called. |
| mCallback.resetLogs(); |
| PackageManager pm = getActivity().getPackageManager(); |
| ComponentName component = new ComponentName(getActivity(), StubTvInputService2.class); |
| assertTrue(PackageManager.COMPONENT_ENABLED_STATE_DISABLED != pm.getComponentEnabledSetting( |
| component)); |
| pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, |
| PackageManager.DONT_KILL_APP); |
| new PollingCheck(TIME_OUT_MS) { |
| @Override |
| protected boolean check() { |
| return mCallback.isInputRemoved(mStubId); |
| } |
| }.run(); |
| |
| // Test if onInputAdded() is called. |
| mCallback.resetLogs(); |
| assertEquals(PackageManager.COMPONENT_ENABLED_STATE_DISABLED, pm.getComponentEnabledSetting( |
| component)); |
| pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, |
| PackageManager.DONT_KILL_APP); |
| new PollingCheck(TIME_OUT_MS) { |
| @Override |
| protected boolean check() { |
| return mCallback.isInputAdded(mStubId); |
| } |
| }.run(); |
| |
| getActivity().runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| mManager.unregisterCallback(mCallback); |
| } |
| }); |
| getInstrumentation().waitForIdleSync(); |
| } |
| |
| public void testTvInputInfoUpdated() throws IOException, XmlPullParserException { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| getActivity().runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| mManager.registerCallback(mCallback, new Handler()); |
| } |
| }); |
| getInstrumentation().waitForIdleSync(); |
| |
| mCallback.resetLogs(); |
| TvInputInfo defaultInfo = new TvInputInfo.Builder(getActivity(), |
| new ComponentName(getActivity(), StubTunerTvInputService.class)).build(); |
| TvInputInfo updatedInfo = new TvInputInfo.Builder(getActivity(), |
| new ComponentName(getActivity(), StubTunerTvInputService.class)) |
| .setTunerCount(10).setCanRecord(true).setCanPauseRecording(false).build(); |
| |
| mManager.updateTvInputInfo(updatedInfo); |
| new PollingCheck(TIME_OUT_MS) { |
| @Override |
| protected boolean check() { |
| TvInputInfo info = mCallback.getLastUpdatedTvInputInfo(); |
| return info != null && info.getTunerCount() == 10 && info.canRecord() |
| && !info.canPauseRecording(); |
| } |
| }.run(); |
| |
| mManager.updateTvInputInfo(defaultInfo); |
| new PollingCheck(TIME_OUT_MS) { |
| @Override |
| protected boolean check() { |
| TvInputInfo info = mCallback.getLastUpdatedTvInputInfo(); |
| return info != null && info.getTunerCount() == 1 && !info.canRecord() |
| && info.canPauseRecording(); |
| } |
| }.run(); |
| |
| getActivity().runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| mManager.unregisterCallback(mCallback); |
| } |
| }); |
| getInstrumentation().waitForIdleSync(); |
| } |
| |
| public void testAcquireTvInputHardware() { |
| if (!Utils.hasTvInputFramework(getActivity()) || mManager == null) { |
| return; |
| } |
| |
| String[] newPermissions = Arrays.copyOf( |
| BASE_SHELL_PERMISSIONS, BASE_SHELL_PERMISSIONS.length + 1); |
| newPermissions[BASE_SHELL_PERMISSIONS.length] = PERMISSION_TV_INPUT_HARDWARE; |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(newPermissions); |
| |
| // Update hardware device list |
| int deviceId = 0; |
| boolean hardwareDeviceAdded = false; |
| List<TvInputHardwareInfo> hardwareList = mManager.getHardwareList(); |
| if (hardwareList == null || hardwareList.isEmpty()) { |
| // Use the test api to add an HDMI hardware device |
| mManager.addHardwareDevice(deviceId); |
| hardwareDeviceAdded = true; |
| } else { |
| deviceId = hardwareList.get(0).getDeviceId(); |
| } |
| |
| // Acquire Hardware with a record client |
| HardwareCallback callback = new HardwareCallback() { |
| @Override |
| public void onReleased() {} |
| |
| @Override |
| public void onStreamConfigChanged(TvStreamConfig[] configs) {} |
| }; |
| CallbackExecutor executor = new CallbackExecutor(); |
| Hardware hardware = mManager.acquireTvInputHardware( |
| deviceId, mStubTvInputInfo, null /*tvInputSessionId*/, |
| TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, |
| executor, callback); |
| assertNotNull(hardware); |
| |
| // Acquire the same device with a LIVE client |
| Hardware hardwareAcquired = mManager.acquireTvInputHardware( |
| deviceId, mStubTvInputInfo, null /*tvInputSessionId*/, |
| TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, |
| executor, callback); |
| |
| assertNotNull(hardwareAcquired); |
| |
| // Clean up |
| if (hardwareDeviceAdded) { |
| mManager.removeHardwareDevice(deviceId); |
| } |
| // Restore the base shell permissions |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(BASE_SHELL_PERMISSIONS); |
| } |
| |
| public void testTvInputHardwareOverrideAudioSink() { |
| if (!Utils.hasTvInputFramework(getActivity()) || mManager == null) { |
| return; |
| } |
| |
| // Update hardware device list |
| int deviceId = 0; |
| boolean hardwareDeviceAdded = false; |
| List<TvInputHardwareInfo> hardwareList = mManager.getHardwareList(); |
| if (hardwareList == null || hardwareList.isEmpty()) { |
| // Use the test api to add an HDMI hardware device |
| mManager.addHardwareDevice(deviceId); |
| hardwareDeviceAdded = true; |
| } else { |
| deviceId = hardwareList.get(0).getDeviceId(); |
| } |
| |
| // Acquire Hardware with a record client |
| HardwareCallback callback = new HardwareCallback() { |
| @Override |
| public void onReleased() { |
| } |
| |
| @Override |
| public void onStreamConfigChanged(TvStreamConfig[] configs) { |
| } |
| }; |
| CallbackExecutor executor = new CallbackExecutor(); |
| Hardware hardware = mManager.acquireTvInputHardware( |
| deviceId, mStubTvInputInfo, null /*tvInputSessionId*/, |
| TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, |
| executor, callback); |
| if (hardware == null) { |
| return; |
| } |
| |
| // Override audio sink |
| try { |
| AudioManager am = mActivity.getSystemService(AudioManager.class); |
| AudioDeviceInfo[] deviceInfos = am.getDevices(AudioManager.GET_DEVICES_OUTPUTS); |
| if (deviceInfos.length > 0) { |
| // test available overrideAudioSink APIs |
| hardware.overrideAudioSink(deviceInfos[0], 0, |
| AudioFormat.CHANNEL_OUT_DEFAULT, AudioFormat.ENCODING_DEFAULT); |
| hardware.overrideAudioSink(deviceInfos[0].getType(), deviceInfos[0].getAddress(), 0, |
| AudioFormat.CHANNEL_OUT_DEFAULT, AudioFormat.ENCODING_DEFAULT); |
| } |
| } catch (Exception e) { |
| fail(); |
| } finally { |
| if (hardwareDeviceAdded) { |
| mManager.removeHardwareDevice(deviceId); |
| } |
| } |
| } |
| |
| public void testGetAvailableExtensionInterfaceNames() { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| |
| try { |
| String inputId = prepareStubHardwareTvInputService(); |
| |
| StubHardwareTvInputService.injectAvailableExtensionInterface( |
| EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION, null); |
| StubHardwareTvInputService.injectAvailableExtensionInterface( |
| EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED, PERMISSION_GRANTED); |
| StubHardwareTvInputService.injectAvailableExtensionInterface( |
| EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED, PERMISSION_UNGRANTED); |
| |
| List<String> names = mManager.getAvailableExtensionInterfaceNames(inputId); |
| assertTrue(names != null && !names.isEmpty()); |
| assertTrue(names.contains(EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION)); |
| assertTrue(names.contains(EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED)); |
| assertFalse(names.contains(EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED)); |
| |
| StubHardwareTvInputService.clearAvailableExtensionInterfaces(); |
| |
| names = mManager.getAvailableExtensionInterfaceNames(inputId); |
| assertTrue(names != null && names.isEmpty()); |
| } finally { |
| StubHardwareTvInputService.clearAvailableExtensionInterfaces(); |
| cleanupStubHardwareTvInputService(); |
| } |
| } |
| |
| public void testGetExtensionInterface() { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| |
| try { |
| String inputId = prepareStubHardwareTvInputService(); |
| |
| StubHardwareTvInputService.injectAvailableExtensionInterface( |
| EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION, null); |
| StubHardwareTvInputService.injectAvailableExtensionInterface( |
| EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED, PERMISSION_GRANTED); |
| StubHardwareTvInputService.injectAvailableExtensionInterface( |
| EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED, PERMISSION_UNGRANTED); |
| |
| assertNotNull(mManager.getExtensionInterface(inputId, |
| EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION)); |
| assertNotNull(mManager.getExtensionInterface(inputId, |
| EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED)); |
| assertNull(mManager.getExtensionInterface(inputId, |
| EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED)); |
| } finally { |
| StubHardwareTvInputService.clearAvailableExtensionInterfaces(); |
| cleanupStubHardwareTvInputService(); |
| } |
| } |
| |
| public void testGetClientPriority() { |
| if (!Utils.hasTvInputFramework(getActivity()) || !Utils.hasTunerFeature(getActivity())) { |
| return; |
| } |
| |
| // Use the test api to get priorities in tunerResourceManagerUseCaseConfig.xml |
| TunerResourceManager trm = (TunerResourceManager) getActivity() |
| .getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE); |
| int fgLivePriority = trm.getConfigPriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, |
| true); |
| int bgLivePriority = trm.getConfigPriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, |
| false); |
| int fgDefaultPriority = trm.getConfigPriority(PRIORITY_HINT_USE_CASE_TYPE_INVALID, true); |
| int bgDefaultPriority = trm.getConfigPriority(PRIORITY_HINT_USE_CASE_TYPE_INVALID, false); |
| boolean isForeground = checkIsForeground(android.os.Process.myPid()); |
| |
| int priority = mManager.getClientPriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); |
| assertTrue(priority == (isForeground ? fgLivePriority : bgLivePriority)); |
| |
| try { |
| priority = mManager.getClientPriority( |
| PRIORITY_HINT_USE_CASE_TYPE_INVALID /* invalid use case type */); |
| } catch (IllegalArgumentException e) { |
| // pass |
| } |
| |
| Handler handler = new Handler(Looper.getMainLooper()); |
| final SessionCallback sessionCallback = new SessionCallback(); |
| mManager.createSession(mStubId, sessionCallback, handler); |
| PollingCheck.waitFor(TIME_OUT_MS, () -> sessionCallback.getSession() != null); |
| Session session = sessionCallback.getSession(); |
| String sessionId = StubTvInputService2.getSessionId(); |
| assertNotNull(sessionId); |
| |
| priority = mManager.getClientPriority( |
| TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, sessionId /* valid sessionId */); |
| assertTrue(priority == (isForeground ? fgLivePriority : bgLivePriority)); |
| |
| try { |
| priority = mManager.getClientPriority( |
| PRIORITY_HINT_USE_CASE_TYPE_INVALID /* invalid use case type */, |
| sessionId /* valid sessionId */); |
| } catch (IllegalArgumentException e) { |
| // pass |
| } |
| |
| session.release(); |
| PollingCheck.waitFor(TIME_OUT_MS, () -> StubTvInputService2.getSessionId() == null); |
| |
| priority = mManager.getClientPriority( |
| TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, sessionId /* invalid sessionId */); |
| assertTrue(priority == bgLivePriority); |
| |
| try { |
| priority = mManager.getClientPriority( |
| PRIORITY_HINT_USE_CASE_TYPE_INVALID /* invalid use case type */, |
| sessionId /* invalid sessionId */); |
| } catch (IllegalArgumentException e) { |
| // pass |
| } |
| } |
| |
| public void testGetClientPid() { |
| if (!Utils.hasTvInputFramework(getActivity())) { |
| return; |
| } |
| |
| Handler handler = new Handler(Looper.getMainLooper()); |
| final SessionCallback sessionCallback = new SessionCallback(); |
| mManager.createSession(mStubId, sessionCallback, handler); |
| PollingCheck.waitFor(TIME_OUT_MS, () -> sessionCallback.getSession() != null); |
| Session session = sessionCallback.getSession(); |
| String sessionId = StubTvInputService2.getSessionId(); |
| assertNotNull(sessionId); |
| |
| int pid = mManager.getClientPid(sessionId); |
| assertTrue(pid == android.os.Process.myPid()); |
| |
| session.release(); |
| PollingCheck.waitFor(TIME_OUT_MS, () -> StubTvInputService2.getSessionId() == null); |
| } |
| |
| private static class LoggingCallback extends TvInputManager.TvInputCallback { |
| private final List<String> mAddedInputs = new ArrayList<>(); |
| private final List<String> mRemovedInputs = new ArrayList<>(); |
| private TvInputInfo mLastUpdatedTvInputInfo; |
| private List<TunedInfo> mTunedInfos; |
| |
| @Override |
| public synchronized void onInputAdded(String inputId) { |
| mAddedInputs.add(inputId); |
| } |
| |
| @Override |
| public synchronized void onInputRemoved(String inputId) { |
| mRemovedInputs.add(inputId); |
| } |
| |
| @Override |
| public synchronized void onTvInputInfoUpdated(TvInputInfo info) { |
| mLastUpdatedTvInputInfo = info; |
| } |
| |
| @Override |
| public synchronized void onCurrentTunedInfosUpdated( |
| List<TunedInfo> tunedInfos) { |
| super.onCurrentTunedInfosUpdated(tunedInfos); |
| mTunedInfos = tunedInfos; |
| } |
| |
| public synchronized void resetLogs() { |
| mAddedInputs.clear(); |
| mRemovedInputs.clear(); |
| mLastUpdatedTvInputInfo = null; |
| } |
| |
| public synchronized boolean isInputAdded(String inputId) { |
| return mRemovedInputs.isEmpty() && mAddedInputs.size() == 1 && mAddedInputs.contains( |
| inputId); |
| } |
| |
| public synchronized boolean isInputRemoved(String inputId) { |
| return mAddedInputs.isEmpty() && mRemovedInputs.size() == 1 && mRemovedInputs.contains( |
| inputId); |
| } |
| |
| public synchronized TvInputInfo getLastUpdatedTvInputInfo() { |
| return mLastUpdatedTvInputInfo; |
| } |
| } |
| |
| public static class StubTvInputService2 extends StubTvInputService { |
| static String sTvInputSessionId; |
| |
| public static String getSessionId() { |
| return sTvInputSessionId; |
| } |
| |
| @Override |
| public Session onCreateSession(String inputId, String tvInputSessionId) { |
| sTvInputSessionId = tvInputSessionId; |
| return new StubSessionImpl2(this); |
| } |
| |
| public static class StubSessionImpl2 extends StubTvInputService.StubSessionImpl { |
| StubSessionImpl2(Context context) { |
| super(context); |
| } |
| |
| @Override |
| public void onRelease() { |
| sTvInputSessionId = null; |
| } |
| } |
| } |
| |
| public static class StubHardwareTvInputService extends TvInputService { |
| private static final Map<String, String> sAvailableExtensionInterfaceMap = new HashMap<>(); |
| |
| private ResolveInfo mResolveInfo = null; |
| private TvInputInfo mTvInputInfo = null; |
| |
| public static void clearAvailableExtensionInterfaces() { |
| sAvailableExtensionInterfaceMap.clear(); |
| } |
| |
| public static void injectAvailableExtensionInterface(String name, String permission) { |
| sAvailableExtensionInterfaceMap.put(name, permission); |
| } |
| |
| @Override |
| public void onCreate() { |
| mResolveInfo = getPackageManager().resolveService( |
| new Intent(SERVICE_INTERFACE).setClass(this, getClass()), |
| PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); |
| } |
| |
| @Override |
| public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) { |
| TvInputInfo info = null; |
| if (hardwareInfo.getDeviceId() == DUMMY_DEVICE_ID) { |
| info = new TvInputInfo.Builder(this, mResolveInfo) |
| .setTvInputHardwareInfo(hardwareInfo) |
| .build(); |
| mTvInputInfo = info; |
| } |
| return info; |
| } |
| |
| @Override |
| public String onHardwareRemoved(TvInputHardwareInfo hardwareInfo) { |
| String inputId = null; |
| if (hardwareInfo.getDeviceId() == DUMMY_DEVICE_ID && mTvInputInfo != null) { |
| inputId = mTvInputInfo.getId(); |
| mTvInputInfo = null; |
| } |
| return inputId; |
| } |
| |
| @Override |
| public Session onCreateSession(String inputId) { |
| return null; |
| } |
| |
| @Override |
| public List<String> getAvailableExtensionInterfaceNames() { |
| super.getAvailableExtensionInterfaceNames(); |
| return new ArrayList<>(sAvailableExtensionInterfaceMap.keySet()); |
| } |
| |
| @Override |
| public String getExtensionInterfacePermission(String name) { |
| super.getExtensionInterfacePermission(name); |
| return sAvailableExtensionInterfaceMap.get(name); |
| } |
| |
| @Override |
| public IBinder getExtensionInterface(String name) { |
| super.getExtensionInterface(name); |
| if (sAvailableExtensionInterfaceMap.containsKey(name)) { |
| return new Binder(); |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| public class CallbackExecutor implements Executor { |
| @Override |
| public void execute(Runnable r) { |
| r.run(); |
| } |
| } |
| |
| private class SessionCallback extends TvInputManager.SessionCallback { |
| private TvInputManager.Session mSession; |
| |
| public TvInputManager.Session getSession() { |
| return mSession; |
| } |
| |
| @Override |
| public void onSessionCreated(TvInputManager.Session session) { |
| mSession = session; |
| } |
| } |
| |
| private boolean checkIsForeground(int pid) { |
| ActivityManager am = (ActivityManager) getActivity() |
| .getSystemService(Context.ACTIVITY_SERVICE); |
| List<RunningAppProcessInfo> appProcesses = am.getRunningAppProcesses(); |
| if (appProcesses == null) { |
| return false; |
| } |
| for (RunningAppProcessInfo appProcess : appProcesses) { |
| if (appProcess.pid == pid |
| && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |