HdmiControlService.java revision d4a94db1cd44a536d535de890a0a14919a39a0dc
1dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project/* 2168021c2827312e17a13d77b54f7d030a08b257bMark Salyzyn * Copyright (C) 2014 The Android Open Source Project 3dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 4dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * you may not use this file except in compliance with the License. 6dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * You may obtain a copy of the License at 7dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 8dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 10dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * See the License for the specific language governing permissions and 14dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * limitations under the License. 15dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 16dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 17dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectpackage com.android.server.hdmi; 18dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 19dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE; 20dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE; 21dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport static com.android.server.hdmi.Constants.DISABLED; 22b5a9890f900a5e2b53e91e6d63f3ade23240f90dKristian Monsenimport static com.android.server.hdmi.Constants.ENABLED; 23b5a9890f900a5e2b53e91e6d63f3ade23240f90dKristian Monsenimport static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_WAKEUP; 24dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport static com.android.server.hdmi.Constants.OPTION_CEC_ENABLE; 25dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport static com.android.server.hdmi.Constants.OPTION_CEC_SERVICE_CONTROL; 26dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE; 27a04464adaf5b95ae953f8577632d3cf8aa2c80a3Mark Salyzynimport static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING; 28a04464adaf5b95ae953f8577632d3cf8aa2c80a3Mark Salyzynimport static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE; 29a04464adaf5b95ae953f8577632d3cf8aa2c80a3Mark Salyzyn 30a04464adaf5b95ae953f8577632d3cf8aa2c80a3Mark Salyzynimport android.annotation.Nullable; 31dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.content.BroadcastReceiver; 32dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.content.ContentResolver; 33dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.content.Context; 34dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.content.Intent; 35dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.content.IntentFilter; 36a04464adaf5b95ae953f8577632d3cf8aa2c80a3Mark Salyzynimport android.database.ContentObserver; 37a04464adaf5b95ae953f8577632d3cf8aa2c80a3Mark Salyzynimport android.hardware.hdmi.HdmiControlManager; 38a04464adaf5b95ae953f8577632d3cf8aa2c80a3Mark Salyzynimport android.hardware.hdmi.HdmiDeviceInfo; 39a04464adaf5b95ae953f8577632d3cf8aa2c80a3Mark Salyzynimport android.hardware.hdmi.HdmiHotplugEvent; 40dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.HdmiPortInfo; 41dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.IHdmiControlCallback; 42dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.IHdmiControlService; 43dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.IHdmiDeviceEventListener; 44dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.IHdmiHotplugEventListener; 45dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.IHdmiInputChangeListener; 46dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.IHdmiMhlVendorCommandListener; 47dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.IHdmiRecordListener; 48dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; 49dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.hardware.hdmi.IHdmiVendorCommandListener; 50dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.media.AudioManager; 51dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.net.Uri; 52dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.Build; 53dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.Handler; 54dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.HandlerThread; 55dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.IBinder; 56dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.Looper; 57dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.PowerManager; 58dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.RemoteException; 59dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.SystemClock; 60dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.SystemProperties; 61dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.os.UserHandle; 62dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.provider.Settings.Global; 63dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.text.TextUtils; 64dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.util.ArraySet; 65dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.util.Slog; 66dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.util.SparseArray; 67dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport android.util.SparseIntArray; 68dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 69dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport com.android.internal.annotations.GuardedBy; 70dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport com.android.internal.util.IndentingPrintWriter; 71dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport com.android.server.SystemService; 72dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; 73dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback; 74dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource; 75dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback; 76dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 77dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport libcore.util.EmptyArray; 78dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 79dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.io.FileDescriptor; 80dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.io.PrintWriter; 81dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.util.ArrayList; 82dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.util.Arrays; 83dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.util.Collections; 84dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.util.List; 85dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectimport java.util.Locale; 86dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 87dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project/** 88dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Provides a service for sending and processing HDMI control messages, 89dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * HDMI-CEC and MHL control command, and providing the information on both standard. 90dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 91dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectpublic final class HdmiControlService extends SystemService { 92dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private static final String TAG = "HdmiControlService"; 93dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 94dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project static final String PERMISSION = "android.permission.HDMI_CEC"; 95dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 96dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // The reason code to initiate intializeCec(). 97dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project static final int INITIATED_BY_ENABLE_CEC = 0; 98dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project static final int INITIATED_BY_BOOT_UP = 1; 99dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project static final int INITIATED_BY_SCREEN_ON = 2; 100dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project static final int INITIATED_BY_WAKE_UP_MESSAGE = 3; 101dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project static final int INITIATED_BY_HOTPLUG = 4; 102dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 103dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 104dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Interface to report send result. 105dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 106dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project interface SendMessageCallback { 107dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 108dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Called when {@link HdmiControlService#sendCecCommand} is completed. 109dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 110dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * @param error result of send request. 111dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * <ul> 112dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * <li>{@link Constants#SEND_RESULT_SUCCESS} 113dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * <li>{@link Constants#SEND_RESULT_NAK} 114dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * <li>{@link Constants#SEND_RESULT_FAILURE} 115dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * </ul> 116dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 117dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project void onSendCompleted(int error); 118dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 119dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 120dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 121dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Interface to get a list of available logical devices. 122dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 123dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project interface DevicePollingCallback { 124dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 125dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Called when device polling is finished. 126dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 127dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * @param ackedAddress a list of logical addresses of available devices 128dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 129dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project void onPollingFinished(List<Integer> ackedAddress); 130dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 131dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 132dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private class HdmiControlBroadcastReceiver extends BroadcastReceiver { 133dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 134dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @Override 135dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project public void onReceive(Context context, Intent intent) { 136dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 137dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project switch (intent.getAction()) { 138dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project case Intent.ACTION_SCREEN_OFF: 139dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (isPowerOnOrTransient()) { 140dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project onStandby(); 141dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 142dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project break; 143dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project case Intent.ACTION_SCREEN_ON: 144dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (isPowerStandbyOrTransient()) { 145dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project onWakeUp(); 146dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 147dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project break; 148dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project case Intent.ACTION_CONFIGURATION_CHANGED: 149dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project String language = Locale.getDefault().getISO3Language(); 150dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (!mLanguage.equals(language)) { 151dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project onLanguageChanged(language); 152dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 153dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project break; 154dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 155dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 156dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 157dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 158dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // A thread to handle synchronous IO of CEC and MHL control service. 159dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms) 160dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // and sparse call it shares a thread to handle IO operations. 161dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread"); 162dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 163dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Used to synchronize the access to the service. 164dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final Object mLock = new Object(); 165dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 166dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Type of logical devices hosted in the system. Stored in the unmodifiable list. 167dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final List<Integer> mLocalDevices; 168dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 169dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // List of records for hotplug event listener to handle the the caller killed in action. 170dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 171dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords = 172dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project new ArrayList<>(); 173dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 174dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // List of records for device event listener to handle the caller killed in action. 175dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 176dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords = 177dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project new ArrayList<>(); 178dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 179dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // List of records for vendor command listener to handle the caller killed in action. 180dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 181dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords = 182dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project new ArrayList<>(); 183dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 184dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 185dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private InputChangeListenerRecord mInputChangeListenerRecord; 186dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 187dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 188dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private HdmiRecordListenerRecord mRecordListenerRecord; 189dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 190dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol 191dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // handling will be disabled and no request will be handled. 192dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 193dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private boolean mHdmiControlEnabled; 194dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 195dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Set to true while the service is in normal mode. While set to false, no input change is 196dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // allowed. Used for situations where input change can confuse users such as channel auto-scan, 197dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // system upgrade, etc., a.k.a. "prohibit mode". 198dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 199dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private boolean mProhibitMode; 200dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 201dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // List of records for system audio mode change to handle the the caller killed in action. 202dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final ArrayList<SystemAudioModeChangeListenerRecord> 203dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mSystemAudioModeChangeListenerRecords = new ArrayList<>(); 204dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 205dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Handler used to run a task in service thread. 206dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final Handler mHandler = new Handler(); 207dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 208dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final SettingsObserver mSettingsObserver; 209dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 210dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final HdmiControlBroadcastReceiver 211dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mHdmiControlBroadcastReceiver = new HdmiControlBroadcastReceiver(); 212dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 213dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @Nullable 214dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private HdmiCecController mCecController; 215dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 216dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // HDMI port information. Stored in the unmodifiable list to keep the static information 217dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // from being modified. 218dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private List<HdmiPortInfo> mPortInfo; 219dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 220dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Map from path(physical address) to port ID. 221dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private UnmodifiableSparseIntArray mPortIdMap; 222dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 223dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Map from port ID to HdmiPortInfo. 224dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap; 225dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 226dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Map from port ID to HdmiDeviceInfo. 227dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap; 228dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 229dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private HdmiCecMessageValidator mMessageValidator; 230dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 231dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 232dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; 233dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 234dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 235dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private String mLanguage = Locale.getDefault().getISO3Language(); 236dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 237dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 238dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private boolean mStandbyMessageReceived = false; 239dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 240dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 241dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private boolean mWakeUpMessageReceived = false; 242dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 243dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 244dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private int mActivePortId = Constants.INVALID_PORT_ID; 245dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 246dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Set to true while the input change by MHL is allowed. 247dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 248dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private boolean mMhlInputChangeEnabled; 249dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 250dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // List of records for MHL Vendor command listener to handle the caller killed in action. 251dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 252dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private final ArrayList<HdmiMhlVendorCommandListenerRecord> 253dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mMhlVendorCommandListenerRecords = new ArrayList<>(); 254dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 255dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @GuardedBy("mLock") 256dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private List<HdmiDeviceInfo> mMhlDevices; 257dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 258dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @Nullable 259dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private HdmiMhlControllerStub mMhlController; 260dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 261dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Last input port before switching to the MHL port. Should switch back to this port 262dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // when the mobile device sends the request one touch play with off. 263dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Gets invalidated if we go to other port/input. 264dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 265dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private int mLastInputMhl = Constants.INVALID_PORT_ID; 266dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 267dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project public HdmiControlService(Context context) { 268dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project super(context); 269dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE)); 270dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mSettingsObserver = new SettingsObserver(mHandler); 271dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 272dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 273dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private static List<Integer> getIntList(String string) { 274dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project ArrayList<Integer> list = new ArrayList<>(); 275dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(','); 276dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project splitter.setString(string); 277dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project for (String item : splitter) { 278dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project try { 279dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project list.add(Integer.parseInt(item)); 280dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } catch (NumberFormatException e) { 281dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Slog.w(TAG, "Can't parseInt: " + item); 282dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 283dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 284dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return Collections.unmodifiableList(list); 285dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 286dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 287dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @Override 288dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project public void onStart() { 289dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mIoThread.start(); 290dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; 291dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mProhibitMode = false; 292dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true); 293dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true); 294dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 295dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mCecController = HdmiCecController.create(this); 296dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (mCecController != null) { 297dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // TODO: Remove this as soon as OEM's HAL implementation is corrected. 298dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mCecController.setOption(OPTION_CEC_ENABLE, ENABLED); 299dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 300dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // TODO: load value for mHdmiControlEnabled from preference. 301dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (mHdmiControlEnabled) { 302dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project initializeCec(INITIATED_BY_BOOT_UP); 303dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 304dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } else { 305dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Slog.i(TAG, "Device does not support HDMI-CEC."); 306dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 307dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 308dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mMhlController = HdmiMhlControllerStub.create(this); 309dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (!mMhlController.isReady()) { 310dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Slog.i(TAG, "Device does not support MHL-control."); 311dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 312dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mMhlDevices = Collections.emptyList(); 313dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 314dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project initPortInfo(); 315dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mMessageValidator = new HdmiCecMessageValidator(this); 316dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService()); 317dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 318dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Register broadcast receiver for power state change. 319dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (mCecController != null) { 320dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project IntentFilter filter = new IntentFilter(); 321dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project filter.addAction(Intent.ACTION_SCREEN_OFF); 322dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project filter.addAction(Intent.ACTION_SCREEN_ON); 323dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 324dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter); 325dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 326dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 327dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 328dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 329168021c2827312e17a13d77b54f7d030a08b257bMark Salyzyn * Called when the initialization of local devices is complete. 330168021c2827312e17a13d77b54f7d030a08b257bMark Salyzyn */ 331dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private void onInitializeCecComplete() { 332168021c2827312e17a13d77b54f7d030a08b257bMark Salyzyn if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) { 333168021c2827312e17a13d77b54f7d030a08b257bMark Salyzyn mPowerStatus = HdmiControlManager.POWER_STATUS_ON; 334dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 335dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mWakeUpMessageReceived = false; 336dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 337dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (isTvDevice()) { 338dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mCecController.setOption(OPTION_CEC_AUTO_WAKEUP, toInt(tv().getAutoWakeup())); 339dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project registerContentObserver(); 340dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 341dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 342dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 343dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private void registerContentObserver() { 344dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project ContentResolver resolver = getContext().getContentResolver(); 345dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project String[] settings = new String[] { 346dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Global.HDMI_CONTROL_ENABLED, 347dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, 348dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, 349dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Global.MHL_INPUT_SWITCHING_ENABLED, 350dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Global.MHL_POWER_CHARGE_ENABLED 351dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project }; 352dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project for (String s : settings) { 353dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver, 354dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project UserHandle.USER_ALL); 355dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 356dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 357dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 358dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private class SettingsObserver extends ContentObserver { 359dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project public SettingsObserver(Handler handler) { 360dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project super(handler); 361dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 362dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 363dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // onChange is set up to run in service thread. 364dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @Override 365dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project public void onChange(boolean selfChange, Uri uri) { 366dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project String option = uri.getLastPathSegment(); 367dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project boolean enabled = readBooleanSetting(option, true); 368dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project switch (option) { 369dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project case Global.HDMI_CONTROL_ENABLED: 370dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project setControlEnabled(enabled); 371dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project break; 372dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED: 373dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project tv().setAutoWakeup(enabled); 374dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project setCecOption(OPTION_CEC_AUTO_WAKEUP, toInt(enabled)); 375dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project break; 376dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED: 377dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project tv().setAutoDeviceOff(enabled); 378dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // No need to propagate to HAL. 379dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project break; 380dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project case Global.MHL_INPUT_SWITCHING_ENABLED: 381dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project setMhlInputChangeEnabled(enabled); 382dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project break; 383dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project case Global.MHL_POWER_CHARGE_ENABLED: 384dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled)); 385dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project break; 386dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 387dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 388dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 389dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 390dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private static int toInt(boolean enabled) { 391dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return enabled ? ENABLED : DISABLED; 392dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 393dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 394dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project boolean readBooleanSetting(String key, boolean defVal) { 395dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project ContentResolver cr = getContext().getContentResolver(); 396dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return Global.getInt(cr, key, toInt(defVal)) == ENABLED; 397dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 398dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 399dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project void writeBooleanSetting(String key, boolean value) { 400dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project ContentResolver cr = getContext().getContentResolver(); 401dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Global.putInt(cr, key, toInt(value)); 402dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 403dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 404dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private void unregisterSettingsObserver() { 405dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project getContext().getContentResolver().unregisterContentObserver(mSettingsObserver); 406dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 407d2c8f52189f8c2a13b88a107c6495f9d83196d2dAndrew Hsieh 408dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private void initializeCec(int initiatedBy) { 409dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED); 410dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project initializeLocalDevices(initiatedBy); 411dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 412dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 413dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 414dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private void initializeLocalDevices(final int initiatedBy) { 415dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 416dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // A container for [Device type, Local device info]. 417dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>(); 418dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project clearLocalDevices(); 419dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project for (int type : mLocalDevices) { 420dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type); 421dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project localDevice.init(); 422dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project localDevices.add(localDevice); 423dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 424dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project allocateLogicalAddress(localDevices, initiatedBy); 425dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 426d2c8f52189f8c2a13b88a107c6495f9d83196d2dAndrew Hsieh 427d2c8f52189f8c2a13b88a107c6495f9d83196d2dAndrew Hsieh @ServiceThreadOnly 428dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private void allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices, 429dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project final int initiatedBy) { 430dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 431dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project final ArrayList<HdmiCecLocalDevice> allocatedDevices = new ArrayList<>(); 432dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project final int[] finished = new int[1]; 433dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project for (final HdmiCecLocalDevice localDevice : allocatingDevices) { 434dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mCecController.allocateLogicalAddress(localDevice.getType(), 435dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project localDevice.getPreferredAddress(), new AllocateAddressCallback() { 436dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @Override 437dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project public void onAllocated(int deviceType, int logicalAddress) { 438dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (logicalAddress == Constants.ADDR_UNREGISTERED) { 439dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]"); 440dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } else { 441dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Set POWER_STATUS_ON to all local devices because they share lifetime 442dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // with system. 443dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType, 444dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HdmiControlManager.POWER_STATUS_ON); 445dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project localDevice.setDeviceInfo(deviceInfo); 446dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mCecController.addLocalDevice(deviceType, localDevice); 447dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mCecController.addLogicalAddress(logicalAddress); 448dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project allocatedDevices.add(localDevice); 449dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 450dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 451dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Address allocation completed for all devices. Notify each device. 452dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (allocatingDevices.size() == ++finished[0]) { 453dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (initiatedBy != INITIATED_BY_HOTPLUG) { 454dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // In case of the hotplug we don't call onInitializeCecComplete() 455dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // since we reallocate the logical address only. 456dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project onInitializeCecComplete(); 457dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 458dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project notifyAddressAllocated(allocatedDevices, initiatedBy); 459dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 460dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 461dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project }); 462dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 4632a7f2ae7d4b25f89e36be04e47b9e7a3d76e0cfdCarl Shapiro } 464dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 465dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 466dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) { 467dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 468dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project for (HdmiCecLocalDevice device : devices) { 469dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project int address = device.getDeviceInfo().getLogicalAddress(); 470dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project device.handleAddressAllocated(address, initiatedBy); 471dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 472dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 473dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 474dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Initialize HDMI port information. Combine the information from CEC and MHL HAL and 475dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // keep them in one place. 476dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 477dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private void initPortInfo() { 478dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 479dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HdmiPortInfo[] cecPortInfo = null; 480dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 481dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // CEC HAL provides majority of the info while MHL does only MHL support flag for 482dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // each port. Return empty array if CEC HAL didn't provide the info. 483dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (mCecController != null) { 484dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project cecPortInfo = mCecController.getPortInfos(); 485dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 486dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (cecPortInfo == null) { 487dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return; 488dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 489dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 490dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>(); 491dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project SparseIntArray portIdMap = new SparseIntArray(); 492dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project SparseArray<HdmiDeviceInfo> portDeviceMap = new SparseArray<>(); 493dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project for (HdmiPortInfo info : cecPortInfo) { 494dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project portIdMap.put(info.getAddress(), info.getId()); 495dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project portInfoMap.put(info.getId(), info); 496dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId())); 497dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 498dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mPortIdMap = new UnmodifiableSparseIntArray(portIdMap); 499dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap); 500dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap); 501dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 502dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos(); 503dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length); 504dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project for (HdmiPortInfo info : mhlPortInfo) { 505dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (info.isMhlSupported()) { 506dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mhlSupportedPorts.add(info.getId()); 507dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 508dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 509dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 510dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // Build HDMI port info list with CEC port info plus MHL supported flag. We can just use 511dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // cec port info if we do not have have port that supports MHL. 512dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (mhlSupportedPorts.isEmpty()) { 513dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo)); 514dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return; 515dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 516dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length); 517dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project for (HdmiPortInfo info : cecPortInfo) { 518dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (mhlSupportedPorts.contains(info.getId())) { 519dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(), 520dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project info.isCecSupported(), true, info.isArcSupported())); 521dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } else { 522dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project result.add(info); 523dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 524dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 525dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mPortInfo = Collections.unmodifiableList(result); 526dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 527dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 528dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project List<HdmiPortInfo> getPortInfo() { 529dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return mPortInfo; 530dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 531dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 532dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 533dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Returns HDMI port information for the given port id. 534dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 535dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * @param portId HDMI port id 536dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * @return {@link HdmiPortInfo} for the given port 537dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 538dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HdmiPortInfo getPortInfo(int portId) { 539dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return mPortInfoMap.get(portId, null); 540dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 541dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 542dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 543dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Returns the routing path (physical address) of the HDMI port for the given 544dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * port id. 545dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 546dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project int portIdToPath(int portId) { 547dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HdmiPortInfo portInfo = getPortInfo(portId); 548dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (portInfo == null) { 549dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Slog.e(TAG, "Cannot find the port info: " + portId); 550dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return Constants.INVALID_PHYSICAL_ADDRESS; 551dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 552dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return portInfo.getAddress(); 553dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 554dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 555dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 556dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Returns the id of HDMI port located at the top of the hierarchy of 557dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance, 558dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * the port id to be returned is the ID associated with the port address 559dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 0x1000 (1.0.0.0) which is the topmost path of the given routing path. 560dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 561dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project int pathToPortId(int path) { 562dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project int portAddress = path & Constants.ROUTING_PATH_TOP_MASK; 563dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID); 564dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 565dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 566dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project boolean isValidPortId(int portId) { 567dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return getPortInfo(portId) != null; 568dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 569dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 570dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 571dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Returns {@link Looper} for IO operation. 572dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 573dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * <p>Declared as package-private. 574dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 575dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Looper getIoLooper() { 576dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return mIoThread.getLooper(); 577dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 578dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 579dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 580dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Returns {@link Looper} of main thread. Use this {@link Looper} instance 581dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * for tasks that are running on main service thread. 582dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 583dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * <p>Declared as package-private. 584dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 585dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project Looper getServiceLooper() { 586dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return mHandler.getLooper(); 587dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 588dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 589dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 590dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Returns physical address of the device. 591dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 592dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project int getPhysicalAddress() { 593dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return mCecController.getPhysicalAddress(); 594dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 595dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 596dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 597dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Returns vendor id of CEC service. 598dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 599dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project int getVendorId() { 600dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return mCecController.getVendorId(); 601dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 602dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 603dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 604dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HdmiDeviceInfo getDeviceInfo(int logicalAddress) { 605dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 606dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HdmiCecLocalDeviceTv tv = tv(); 607dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (tv == null) { 608dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return null; 609dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 610dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return tv.getCecDeviceInfo(logicalAddress); 611dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 612dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 613dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 614dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Returns version of CEC. 615dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 616dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project int getCecVersion() { 617dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return mCecController.getVersion(); 618dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 619dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 620a04464adaf5b95ae953f8577632d3cf8aa2c80a3Mark Salyzyn /** 621dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Whether a device of the specified physical address is connected to ARC enabled port. 622dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 623dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project boolean isConnectedToArcPort(int physicalAddress) { 624dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project int portId = pathToPortId(physicalAddress); 625dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (portId != Constants.INVALID_PORT_ID) { 626dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return mPortInfoMap.get(portId).isArcSupported(); 627dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 628dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return false; 629dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 630dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 631dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project void runOnServiceThread(Runnable runnable) { 632dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mHandler.post(runnable); 633dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 634dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 635dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project void runOnServiceThreadAtFrontOfQueue(Runnable runnable) { 636dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mHandler.postAtFrontOfQueue(runnable); 637dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 638dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 639dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project private void assertRunOnServiceThread() { 640dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (Looper.myLooper() != mHandler.getLooper()) { 641dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project throw new IllegalStateException("Should run on service thread."); 642dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 643dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 644dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 645dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 646dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Transmit a CEC command to CEC bus. 647dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 648dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * @param command CEC command to send out 649dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * @param callback interface used to the result of send command 650dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 651dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 652dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) { 653dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 654dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (mMessageValidator.isValid(command) == HdmiCecMessageValidator.OK) { 655dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mCecController.sendCommand(command, callback); 656dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } else { 657dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HdmiLogger.error("Invalid message type:" + command); 658dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (callback != null) { 659dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project callback.onSendCompleted(Constants.SEND_RESULT_FAILURE); 660dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 661dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 662dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 663dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 664dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 665dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project void sendCecCommand(HdmiCecMessage command) { 666dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 667dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project sendCecCommand(command, null); 668dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 669dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 670dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project /** 671dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Send <Feature Abort> command on the given CEC message if possible. 672dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * If the aborted message is invalid, then it wont send the message. 673dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * @param command original command to be aborted 674dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * @param reason reason of feature abort 675dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 676dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 677dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) { 678dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 679dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mCecController.maySendFeatureAbortCommand(command, reason); 680dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 681dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 682dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project @ServiceThreadOnly 683dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project boolean handleCecCommand(HdmiCecMessage message) { 684dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project assertRunOnServiceThread(); 685dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project int errorCode = mMessageValidator.isValid(message); 686dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (errorCode != HdmiCecMessageValidator.OK) { 687dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project // We'll not response on the messages with the invalid source or destination. 688dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project if (errorCode == HdmiCecMessageValidator.ERROR_PARAMETER) { 689dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND); 690dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project } 691dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project return true; 692 } 693 return dispatchMessageToLocalDevice(message); 694 } 695 696 void setAudioReturnChannel(boolean enabled) { 697 mCecController.setAudioReturnChannel(enabled); 698 } 699 700 @ServiceThreadOnly 701 private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) { 702 assertRunOnServiceThread(); 703 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 704 if (device.dispatchMessage(message) 705 && message.getDestination() != Constants.ADDR_BROADCAST) { 706 return true; 707 } 708 } 709 710 if (message.getDestination() != Constants.ADDR_BROADCAST) { 711 HdmiLogger.warning("Unhandled cec command:" + message); 712 } 713 return false; 714 } 715 716 /** 717 * Called when a new hotplug event is issued. 718 * 719 * @param portId hdmi port number where hot plug event issued. 720 * @param connected whether to be plugged in or not 721 */ 722 @ServiceThreadOnly 723 void onHotplug(int portId, boolean connected) { 724 assertRunOnServiceThread(); 725 726 ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>(); 727 for (int type : mLocalDevices) { 728 if (type == HdmiDeviceInfo.DEVICE_TV) { 729 // Skip the reallocation of the logical address on TV. 730 continue; 731 } 732 HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type); 733 if (localDevice == null) { 734 localDevice = HdmiCecLocalDevice.create(this, type); 735 localDevice.init(); 736 } 737 localDevices.add(localDevice); 738 } 739 allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG); 740 741 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 742 device.onHotplug(portId, connected); 743 } 744 announceHotplugEvent(portId, connected); 745 } 746 747 /** 748 * Poll all remote devices. It sends <Polling Message> to all remote 749 * devices. 750 * 751 * @param callback an interface used to get a list of all remote devices' address 752 * @param sourceAddress a logical address of source device where sends polling message 753 * @param pickStrategy strategy how to pick polling candidates 754 * @param retryCount the number of retry used to send polling message to remote devices 755 * @throw IllegalArgumentException if {@code pickStrategy} is invalid value 756 */ 757 @ServiceThreadOnly 758 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy, 759 int retryCount) { 760 assertRunOnServiceThread(); 761 mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy), 762 retryCount); 763 } 764 765 private int checkPollStrategy(int pickStrategy) { 766 int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK; 767 if (strategy == 0) { 768 throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy); 769 } 770 int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK; 771 if (iterationStrategy == 0) { 772 throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy); 773 } 774 return strategy | iterationStrategy; 775 } 776 777 List<HdmiCecLocalDevice> getAllLocalDevices() { 778 assertRunOnServiceThread(); 779 return mCecController.getLocalDeviceList(); 780 } 781 782 Object getServiceLock() { 783 return mLock; 784 } 785 786 void setAudioStatus(boolean mute, int volume) { 787 AudioManager audioManager = getAudioManager(); 788 boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC); 789 if (mute) { 790 if (!muted) { 791 audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true); 792 } 793 } else { 794 if (muted) { 795 audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false); 796 } 797 // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing 798 // volume change notification back to hdmi control service. 799 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 800 AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME); 801 } 802 } 803 804 void announceSystemAudioModeChange(boolean enabled) { 805 synchronized (mLock) { 806 for (SystemAudioModeChangeListenerRecord record : 807 mSystemAudioModeChangeListenerRecords) { 808 invokeSystemAudioModeChangeLocked(record.mListener, enabled); 809 } 810 } 811 } 812 813 private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) { 814 // TODO: find better name instead of model name. 815 String displayName = Build.MODEL; 816 return new HdmiDeviceInfo(logicalAddress, 817 getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType, 818 getVendorId(), displayName); 819 } 820 821 @ServiceThreadOnly 822 void sendMhlSubcommand(int portId, HdmiMhlSubcommand command) { 823 assertRunOnServiceThread(); 824 sendMhlSubcommand(portId, command, null); 825 } 826 827 @ServiceThreadOnly 828 void sendMhlSubcommand(int portId, HdmiMhlSubcommand command, SendMessageCallback callback) { 829 assertRunOnServiceThread(); 830 mMhlController.sendSubcommand(portId, command, callback); 831 } 832 833 @ServiceThreadOnly 834 boolean handleMhlSubcommand(int portId, HdmiMhlSubcommand message) { 835 assertRunOnServiceThread(); 836 837 HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId); 838 if (device != null) { 839 return device.handleSubcommand(message); 840 } 841 Slog.w(TAG, "No mhl device exists[portId:" + portId + ", message:" + message); 842 return false; 843 } 844 845 @ServiceThreadOnly 846 void handleMhlHotplugEvent(int portId, boolean connected) { 847 assertRunOnServiceThread(); 848 if (connected) { 849 HdmiMhlLocalDevice newDevice = new HdmiMhlLocalDevice(this, portId); 850 HdmiMhlLocalDevice oldDevice = mMhlController.addLocalDevice(newDevice); 851 if (oldDevice != null) { 852 oldDevice.onDeviceRemoved(); 853 Slog.i(TAG, "Old device of port " + portId + " is removed"); 854 } 855 } else { 856 HdmiMhlLocalDevice device = mMhlController.removeLocalDevice(portId); 857 if (device != null) { 858 device.onDeviceRemoved(); 859 // There is no explicit event for device removal unlike capability register event 860 // used for device addition . Hence we remove the device on hotplug event. 861 HdmiDeviceInfo deviceInfo = device.getInfo(); 862 if (deviceInfo != null) { 863 invokeDeviceEventListeners(deviceInfo, DEVICE_EVENT_REMOVE_DEVICE); 864 updateSafeMhlInput(); 865 } 866 } else { 867 Slog.w(TAG, "No device to remove:[portId=" + portId); 868 } 869 } 870 announceHotplugEvent(portId, connected); 871 } 872 873 @ServiceThreadOnly 874 void handleMhlCbusModeChanged(int portId, int cbusmode) { 875 assertRunOnServiceThread(); 876 HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId); 877 if (device != null) { 878 device.setCbusMode(cbusmode); 879 } else { 880 Slog.w(TAG, "No mhl device exists for cbus mode change[portId:" + portId + 881 ", cbusmode:" + cbusmode + "]"); 882 } 883 } 884 885 @ServiceThreadOnly 886 void handleMhlVbusOvercurrent(int portId, boolean on) { 887 assertRunOnServiceThread(); 888 HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId); 889 if (device != null) { 890 device.onVbusOvercurrentDetected(on); 891 } else { 892 Slog.w(TAG, "No mhl device exists for vbus overcurrent event[portId:" + portId + "]"); 893 } 894 } 895 896 @ServiceThreadOnly 897 void handleMhlCapabilityRegisterChanged(int portId, int adopterId, int deviceId) { 898 assertRunOnServiceThread(); 899 HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId); 900 901 // Hotplug event should already have been called before capability register change event. 902 if (device != null) { 903 device.setCapabilityRegister(adopterId, deviceId); 904 invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_ADD_DEVICE); 905 updateSafeMhlInput(); 906 } else { 907 Slog.w(TAG, "No mhl device exists for capability register change event[portId:" 908 + portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]"); 909 } 910 } 911 912 @ServiceThreadOnly 913 private void updateSafeMhlInput() { 914 assertRunOnServiceThread(); 915 List<HdmiDeviceInfo> inputs = Collections.emptyList(); 916 SparseArray<HdmiMhlLocalDevice> devices = mMhlController.getAllLocalDevices(); 917 for (int i = 0; i < devices.size(); ++i) { 918 HdmiMhlLocalDevice device = devices.valueAt(i); 919 HdmiDeviceInfo info = device.getInfo(); 920 if (info != null) { 921 if (inputs.isEmpty()) { 922 inputs = new ArrayList<>(); 923 } 924 inputs.add(device.getInfo()); 925 } 926 } 927 synchronized (mLock) { 928 mMhlDevices = inputs; 929 } 930 } 931 932 private List<HdmiDeviceInfo> getMhlDevicesLocked() { 933 return mMhlDevices; 934 } 935 936 private class HdmiMhlVendorCommandListenerRecord implements IBinder.DeathRecipient { 937 private final IHdmiMhlVendorCommandListener mListener; 938 939 public HdmiMhlVendorCommandListenerRecord(IHdmiMhlVendorCommandListener listener) { 940 mListener = listener; 941 } 942 943 @Override 944 public void binderDied() { 945 mMhlVendorCommandListenerRecords.remove(this); 946 } 947 } 948 949 // Record class that monitors the event of the caller of being killed. Used to clean up 950 // the listener list and record list accordingly. 951 private final class HotplugEventListenerRecord implements IBinder.DeathRecipient { 952 private final IHdmiHotplugEventListener mListener; 953 954 public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) { 955 mListener = listener; 956 } 957 958 @Override 959 public void binderDied() { 960 synchronized (mLock) { 961 mHotplugEventListenerRecords.remove(this); 962 } 963 } 964 } 965 966 private final class DeviceEventListenerRecord implements IBinder.DeathRecipient { 967 private final IHdmiDeviceEventListener mListener; 968 969 public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) { 970 mListener = listener; 971 } 972 973 @Override 974 public void binderDied() { 975 synchronized (mLock) { 976 mDeviceEventListenerRecords.remove(this); 977 } 978 } 979 } 980 981 private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient { 982 private final IHdmiSystemAudioModeChangeListener mListener; 983 984 public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) { 985 mListener = listener; 986 } 987 988 @Override 989 public void binderDied() { 990 synchronized (mLock) { 991 mSystemAudioModeChangeListenerRecords.remove(this); 992 } 993 } 994 } 995 996 class VendorCommandListenerRecord implements IBinder.DeathRecipient { 997 private final IHdmiVendorCommandListener mListener; 998 private final int mDeviceType; 999 1000 public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) { 1001 mListener = listener; 1002 mDeviceType = deviceType; 1003 } 1004 1005 @Override 1006 public void binderDied() { 1007 synchronized (mLock) { 1008 mVendorCommandListenerRecords.remove(this); 1009 } 1010 } 1011 } 1012 1013 private class HdmiRecordListenerRecord implements IBinder.DeathRecipient { 1014 private final IHdmiRecordListener mListener; 1015 1016 public HdmiRecordListenerRecord(IHdmiRecordListener listener) { 1017 mListener = listener; 1018 } 1019 1020 @Override 1021 public void binderDied() { 1022 synchronized (mLock) { 1023 mRecordListenerRecord = null; 1024 } 1025 } 1026 } 1027 1028 private void enforceAccessPermission() { 1029 getContext().enforceCallingOrSelfPermission(PERMISSION, TAG); 1030 } 1031 1032 private final class BinderService extends IHdmiControlService.Stub { 1033 @Override 1034 public int[] getSupportedTypes() { 1035 enforceAccessPermission(); 1036 // mLocalDevices is an unmodifiable list - no lock necesary. 1037 int[] localDevices = new int[mLocalDevices.size()]; 1038 for (int i = 0; i < localDevices.length; ++i) { 1039 localDevices[i] = mLocalDevices.get(i); 1040 } 1041 return localDevices; 1042 } 1043 1044 @Override 1045 public HdmiDeviceInfo getActiveSource() { 1046 HdmiCecLocalDeviceTv tv = tv(); 1047 if (tv == null) { 1048 Slog.w(TAG, "Local tv device not available"); 1049 return null; 1050 } 1051 ActiveSource activeSource = tv.getActiveSource(); 1052 if (activeSource.isValid()) { 1053 return new HdmiDeviceInfo(activeSource.logicalAddress, 1054 activeSource.physicalAddress, HdmiDeviceInfo.PORT_INVALID, 1055 HdmiDeviceInfo.DEVICE_INACTIVE, 0, ""); 1056 } 1057 int activePath = tv.getActivePath(); 1058 if (activePath != HdmiDeviceInfo.PATH_INVALID) { 1059 return new HdmiDeviceInfo(activePath, tv.getActivePortId()); 1060 } 1061 return null; 1062 } 1063 1064 @Override 1065 public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) { 1066 enforceAccessPermission(); 1067 runOnServiceThread(new Runnable() { 1068 @Override 1069 public void run() { 1070 if (callback == null) { 1071 Slog.e(TAG, "Callback cannot be null"); 1072 return; 1073 } 1074 HdmiCecLocalDeviceTv tv = tv(); 1075 if (tv == null) { 1076 Slog.w(TAG, "Local tv device not available"); 1077 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 1078 return; 1079 } 1080 HdmiMhlLocalDevice device = mMhlController.getLocalDeviceById(deviceId); 1081 if (device != null) { 1082 if (device.getPortId() == tv.getActivePortId()) { 1083 invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS); 1084 return; 1085 } 1086 // Upon selecting MHL device, we send RAP[Content On] to wake up 1087 // the connected mobile device, start routing control to switch ports. 1088 // callback is handled by MHL action. 1089 device.turnOn(callback); 1090 tv.doManualPortSwitching(device.getPortId(), null); 1091 return; 1092 } 1093 tv.deviceSelect(deviceId, callback); 1094 } 1095 }); 1096 } 1097 1098 @Override 1099 public void portSelect(final int portId, final IHdmiControlCallback callback) { 1100 enforceAccessPermission(); 1101 runOnServiceThread(new Runnable() { 1102 @Override 1103 public void run() { 1104 if (callback == null) { 1105 Slog.e(TAG, "Callback cannot be null"); 1106 return; 1107 } 1108 HdmiCecLocalDeviceTv tv = tv(); 1109 if (tv == null) { 1110 Slog.w(TAG, "Local tv device not available"); 1111 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 1112 return; 1113 } 1114 tv.doManualPortSwitching(portId, callback); 1115 } 1116 }); 1117 } 1118 1119 @Override 1120 public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) { 1121 enforceAccessPermission(); 1122 runOnServiceThread(new Runnable() { 1123 @Override 1124 public void run() { 1125 HdmiMhlLocalDevice device = mMhlController.getLocalDevice(mActivePortId); 1126 if (device != null) { 1127 device.sendKeyEvent(keyCode, isPressed); 1128 return; 1129 } 1130 if (mCecController != null) { 1131 HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType); 1132 if (localDevice == null) { 1133 Slog.w(TAG, "Local device not available"); 1134 return; 1135 } 1136 localDevice.sendKeyEvent(keyCode, isPressed); 1137 } 1138 } 1139 }); 1140 } 1141 1142 @Override 1143 public void oneTouchPlay(final IHdmiControlCallback callback) { 1144 enforceAccessPermission(); 1145 runOnServiceThread(new Runnable() { 1146 @Override 1147 public void run() { 1148 HdmiControlService.this.oneTouchPlay(callback); 1149 } 1150 }); 1151 } 1152 1153 @Override 1154 public void queryDisplayStatus(final IHdmiControlCallback callback) { 1155 enforceAccessPermission(); 1156 runOnServiceThread(new Runnable() { 1157 @Override 1158 public void run() { 1159 HdmiControlService.this.queryDisplayStatus(callback); 1160 } 1161 }); 1162 } 1163 1164 @Override 1165 public void addHotplugEventListener(final IHdmiHotplugEventListener listener) { 1166 enforceAccessPermission(); 1167 HdmiControlService.this.addHotplugEventListener(listener); 1168 } 1169 1170 @Override 1171 public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) { 1172 enforceAccessPermission(); 1173 HdmiControlService.this.removeHotplugEventListener(listener); 1174 } 1175 1176 @Override 1177 public void addDeviceEventListener(final IHdmiDeviceEventListener listener) { 1178 enforceAccessPermission(); 1179 HdmiControlService.this.addDeviceEventListener(listener); 1180 } 1181 1182 @Override 1183 public List<HdmiPortInfo> getPortInfo() { 1184 enforceAccessPermission(); 1185 return HdmiControlService.this.getPortInfo(); 1186 } 1187 1188 @Override 1189 public boolean canChangeSystemAudioMode() { 1190 enforceAccessPermission(); 1191 HdmiCecLocalDeviceTv tv = tv(); 1192 if (tv == null) { 1193 return false; 1194 } 1195 return tv.hasSystemAudioDevice(); 1196 } 1197 1198 @Override 1199 public boolean getSystemAudioMode() { 1200 enforceAccessPermission(); 1201 HdmiCecLocalDeviceTv tv = tv(); 1202 if (tv == null) { 1203 return false; 1204 } 1205 return tv.isSystemAudioActivated(); 1206 } 1207 1208 @Override 1209 public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) { 1210 enforceAccessPermission(); 1211 runOnServiceThread(new Runnable() { 1212 @Override 1213 public void run() { 1214 HdmiCecLocalDeviceTv tv = tv(); 1215 if (tv == null) { 1216 Slog.w(TAG, "Local tv device not available"); 1217 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 1218 return; 1219 } 1220 tv.changeSystemAudioMode(enabled, callback); 1221 } 1222 }); 1223 } 1224 1225 @Override 1226 public void addSystemAudioModeChangeListener( 1227 final IHdmiSystemAudioModeChangeListener listener) { 1228 enforceAccessPermission(); 1229 HdmiControlService.this.addSystemAudioModeChangeListner(listener); 1230 } 1231 1232 @Override 1233 public void removeSystemAudioModeChangeListener( 1234 final IHdmiSystemAudioModeChangeListener listener) { 1235 enforceAccessPermission(); 1236 HdmiControlService.this.removeSystemAudioModeChangeListener(listener); 1237 } 1238 1239 @Override 1240 public void setInputChangeListener(final IHdmiInputChangeListener listener) { 1241 enforceAccessPermission(); 1242 HdmiControlService.this.setInputChangeListener(listener); 1243 } 1244 1245 @Override 1246 public List<HdmiDeviceInfo> getInputDevices() { 1247 enforceAccessPermission(); 1248 // No need to hold the lock for obtaining TV device as the local device instance 1249 // is preserved while the HDMI control is enabled. 1250 HdmiCecLocalDeviceTv tv = tv(); 1251 synchronized (mLock) { 1252 List<HdmiDeviceInfo> cecDevices = (tv == null) 1253 ? Collections.<HdmiDeviceInfo>emptyList() 1254 : tv.getSafeExternalInputsLocked(); 1255 return HdmiUtils.mergeToUnmodifiableList(cecDevices, getMhlDevicesLocked()); 1256 } 1257 } 1258 1259 @Override 1260 public void setSystemAudioVolume(final int oldIndex, final int newIndex, 1261 final int maxIndex) { 1262 enforceAccessPermission(); 1263 runOnServiceThread(new Runnable() { 1264 @Override 1265 public void run() { 1266 HdmiCecLocalDeviceTv tv = tv(); 1267 if (tv == null) { 1268 Slog.w(TAG, "Local tv device not available"); 1269 return; 1270 } 1271 tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex); 1272 } 1273 }); 1274 } 1275 1276 @Override 1277 public void setSystemAudioMute(final boolean mute) { 1278 enforceAccessPermission(); 1279 runOnServiceThread(new Runnable() { 1280 @Override 1281 public void run() { 1282 HdmiCecLocalDeviceTv tv = tv(); 1283 if (tv == null) { 1284 Slog.w(TAG, "Local tv device not available"); 1285 return; 1286 } 1287 tv.changeMute(mute); 1288 } 1289 }); 1290 } 1291 1292 @Override 1293 public void setArcMode(final boolean enabled) { 1294 enforceAccessPermission(); 1295 runOnServiceThread(new Runnable() { 1296 @Override 1297 public void run() { 1298 HdmiCecLocalDeviceTv tv = tv(); 1299 if (tv == null) { 1300 Slog.w(TAG, "Local tv device not available to change arc mode."); 1301 return; 1302 } 1303 } 1304 }); 1305 } 1306 1307 @Override 1308 public void setProhibitMode(final boolean enabled) { 1309 enforceAccessPermission(); 1310 if (!isTvDevice()) { 1311 return; 1312 } 1313 HdmiControlService.this.setProhibitMode(enabled); 1314 } 1315 1316 @Override 1317 public void addVendorCommandListener(final IHdmiVendorCommandListener listener, 1318 final int deviceType) { 1319 enforceAccessPermission(); 1320 HdmiControlService.this.addVendorCommandListener(listener, deviceType); 1321 } 1322 1323 @Override 1324 public void sendVendorCommand(final int deviceType, final int targetAddress, 1325 final byte[] params, final boolean hasVendorId) { 1326 enforceAccessPermission(); 1327 runOnServiceThread(new Runnable() { 1328 @Override 1329 public void run() { 1330 HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType); 1331 if (device == null) { 1332 Slog.w(TAG, "Local device not available"); 1333 return; 1334 } 1335 if (hasVendorId) { 1336 sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId( 1337 device.getDeviceInfo().getLogicalAddress(), targetAddress, 1338 getVendorId(), params)); 1339 } else { 1340 sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand( 1341 device.getDeviceInfo().getLogicalAddress(), targetAddress, params)); 1342 } 1343 } 1344 }); 1345 } 1346 1347 @Override 1348 public void sendStandby(final int deviceType, final int deviceId) { 1349 enforceAccessPermission(); 1350 runOnServiceThread(new Runnable() { 1351 @Override 1352 public void run() { 1353 HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType); 1354 if (device == null) { 1355 Slog.w(TAG, "Local device not available"); 1356 return; 1357 } 1358 device.sendStandby(deviceId); 1359 } 1360 }); 1361 } 1362 1363 @Override 1364 public void setHdmiRecordListener(IHdmiRecordListener listener) { 1365 HdmiControlService.this.setHdmiRecordListener(listener); 1366 } 1367 1368 @Override 1369 public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) { 1370 runOnServiceThread(new Runnable() { 1371 @Override 1372 public void run() { 1373 if (!isTvDevice()) { 1374 Slog.w(TAG, "No TV is available."); 1375 return; 1376 } 1377 tv().startOneTouchRecord(recorderAddress, recordSource); 1378 } 1379 }); 1380 } 1381 1382 @Override 1383 public void stopOneTouchRecord(final int recorderAddress) { 1384 runOnServiceThread(new Runnable() { 1385 @Override 1386 public void run() { 1387 if (!isTvDevice()) { 1388 Slog.w(TAG, "No TV is available."); 1389 return; 1390 } 1391 tv().stopOneTouchRecord(recorderAddress); 1392 } 1393 }); 1394 } 1395 1396 @Override 1397 public void startTimerRecording(final int recorderAddress, final int sourceType, 1398 final byte[] recordSource) { 1399 runOnServiceThread(new Runnable() { 1400 @Override 1401 public void run() { 1402 if (!isTvDevice()) { 1403 Slog.w(TAG, "No TV is available."); 1404 return; 1405 } 1406 tv().startTimerRecording(recorderAddress, sourceType, recordSource); 1407 } 1408 }); 1409 } 1410 1411 @Override 1412 public void clearTimerRecording(final int recorderAddress, final int sourceType, 1413 final byte[] recordSource) { 1414 runOnServiceThread(new Runnable() { 1415 @Override 1416 public void run() { 1417 if (!isTvDevice()) { 1418 Slog.w(TAG, "No TV is available."); 1419 return; 1420 } 1421 tv().clearTimerRecording(recorderAddress, sourceType, recordSource); 1422 } 1423 }); 1424 } 1425 1426 @Override 1427 public void sendMhlVendorCommand(final int portId, final int offset, final int length, 1428 final byte[] data) { 1429 enforceAccessPermission(); 1430 runOnServiceThread(new Runnable() { 1431 @Override 1432 public void run() { 1433 if (!isControlEnabled()) { 1434 Slog.w(TAG, "Hdmi control is disabled."); 1435 return ; 1436 } 1437 HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId); 1438 if (device == null) { 1439 Slog.w(TAG, "Invalid port id:" + portId); 1440 return; 1441 } 1442 mMhlController.sendVendorCommand(portId, offset, length, data); 1443 } 1444 }); 1445 } 1446 1447 @Override 1448 public void addHdmiMhlVendorCommandListener( 1449 IHdmiMhlVendorCommandListener listener) { 1450 enforceAccessPermission(); 1451 HdmiControlService.this.addHdmiMhlVendorCommandListener(listener); 1452 } 1453 1454 @Override 1455 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 1456 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 1457 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 1458 1459 pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled); 1460 pw.println("mProhibitMode: " + mProhibitMode); 1461 if (mCecController != null) { 1462 pw.println("mCecController: "); 1463 pw.increaseIndent(); 1464 mCecController.dump(pw); 1465 pw.decreaseIndent(); 1466 } 1467 pw.println("mPortInfo: "); 1468 pw.increaseIndent(); 1469 for (HdmiPortInfo hdmiPortInfo : mPortInfo) { 1470 pw.println("- " + hdmiPortInfo); 1471 } 1472 pw.decreaseIndent(); 1473 pw.println("mPowerStatus: " + mPowerStatus); 1474 } 1475 } 1476 1477 @ServiceThreadOnly 1478 private void oneTouchPlay(final IHdmiControlCallback callback) { 1479 assertRunOnServiceThread(); 1480 HdmiCecLocalDevicePlayback source = playback(); 1481 if (source == null) { 1482 Slog.w(TAG, "Local playback device not available"); 1483 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 1484 return; 1485 } 1486 source.oneTouchPlay(callback); 1487 } 1488 1489 @ServiceThreadOnly 1490 private void queryDisplayStatus(final IHdmiControlCallback callback) { 1491 assertRunOnServiceThread(); 1492 HdmiCecLocalDevicePlayback source = playback(); 1493 if (source == null) { 1494 Slog.w(TAG, "Local playback device not available"); 1495 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 1496 return; 1497 } 1498 source.queryDisplayStatus(callback); 1499 } 1500 1501 private void addHotplugEventListener(IHdmiHotplugEventListener listener) { 1502 HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener); 1503 try { 1504 listener.asBinder().linkToDeath(record, 0); 1505 } catch (RemoteException e) { 1506 Slog.w(TAG, "Listener already died"); 1507 return; 1508 } 1509 synchronized (mLock) { 1510 mHotplugEventListenerRecords.add(record); 1511 } 1512 } 1513 1514 private void removeHotplugEventListener(IHdmiHotplugEventListener listener) { 1515 synchronized (mLock) { 1516 for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) { 1517 if (record.mListener.asBinder() == listener.asBinder()) { 1518 listener.asBinder().unlinkToDeath(record, 0); 1519 mHotplugEventListenerRecords.remove(record); 1520 break; 1521 } 1522 } 1523 } 1524 } 1525 1526 private void addDeviceEventListener(IHdmiDeviceEventListener listener) { 1527 DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener); 1528 try { 1529 listener.asBinder().linkToDeath(record, 0); 1530 } catch (RemoteException e) { 1531 Slog.w(TAG, "Listener already died"); 1532 return; 1533 } 1534 synchronized (mLock) { 1535 mDeviceEventListenerRecords.add(record); 1536 } 1537 } 1538 1539 void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) { 1540 synchronized (mLock) { 1541 for (DeviceEventListenerRecord record : mDeviceEventListenerRecords) { 1542 try { 1543 record.mListener.onStatusChanged(device, status); 1544 } catch (RemoteException e) { 1545 Slog.e(TAG, "Failed to report device event:" + e); 1546 } 1547 } 1548 } 1549 } 1550 1551 private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) { 1552 SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord( 1553 listener); 1554 try { 1555 listener.asBinder().linkToDeath(record, 0); 1556 } catch (RemoteException e) { 1557 Slog.w(TAG, "Listener already died"); 1558 return; 1559 } 1560 synchronized (mLock) { 1561 mSystemAudioModeChangeListenerRecords.add(record); 1562 } 1563 } 1564 1565 private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) { 1566 synchronized (mLock) { 1567 for (SystemAudioModeChangeListenerRecord record : 1568 mSystemAudioModeChangeListenerRecords) { 1569 if (record.mListener.asBinder() == listener) { 1570 listener.asBinder().unlinkToDeath(record, 0); 1571 mSystemAudioModeChangeListenerRecords.remove(record); 1572 break; 1573 } 1574 } 1575 } 1576 } 1577 1578 private final class InputChangeListenerRecord implements IBinder.DeathRecipient { 1579 private final IHdmiInputChangeListener mListener; 1580 1581 public InputChangeListenerRecord(IHdmiInputChangeListener listener) { 1582 mListener = listener; 1583 } 1584 1585 @Override 1586 public void binderDied() { 1587 synchronized (mLock) { 1588 mInputChangeListenerRecord = null; 1589 } 1590 } 1591 } 1592 1593 private void setInputChangeListener(IHdmiInputChangeListener listener) { 1594 synchronized (mLock) { 1595 mInputChangeListenerRecord = new InputChangeListenerRecord(listener); 1596 try { 1597 listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0); 1598 } catch (RemoteException e) { 1599 Slog.w(TAG, "Listener already died"); 1600 return; 1601 } 1602 } 1603 } 1604 1605 void invokeInputChangeListener(HdmiDeviceInfo info) { 1606 synchronized (mLock) { 1607 if (mInputChangeListenerRecord != null) { 1608 try { 1609 mInputChangeListenerRecord.mListener.onChanged(info); 1610 } catch (RemoteException e) { 1611 Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e); 1612 } 1613 } 1614 } 1615 } 1616 1617 private void setHdmiRecordListener(IHdmiRecordListener listener) { 1618 synchronized (mLock) { 1619 mRecordListenerRecord = new HdmiRecordListenerRecord(listener); 1620 try { 1621 listener.asBinder().linkToDeath(mRecordListenerRecord, 0); 1622 } catch (RemoteException e) { 1623 Slog.w(TAG, "Listener already died.", e); 1624 } 1625 } 1626 } 1627 1628 byte[] invokeRecordRequestListener(int recorderAddress) { 1629 synchronized (mLock) { 1630 if (mRecordListenerRecord != null) { 1631 try { 1632 return mRecordListenerRecord.mListener.getOneTouchRecordSource(recorderAddress); 1633 } catch (RemoteException e) { 1634 Slog.w(TAG, "Failed to start record.", e); 1635 } 1636 } 1637 return EmptyArray.BYTE; 1638 } 1639 } 1640 1641 void invokeOneTouchRecordResult(int result) { 1642 synchronized (mLock) { 1643 if (mRecordListenerRecord != null) { 1644 try { 1645 mRecordListenerRecord.mListener.onOneTouchRecordResult(result); 1646 } catch (RemoteException e) { 1647 Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e); 1648 } 1649 } 1650 } 1651 } 1652 1653 void invokeTimerRecordingResult(int result) { 1654 synchronized (mLock) { 1655 if (mRecordListenerRecord != null) { 1656 try { 1657 mRecordListenerRecord.mListener.onTimerRecordingResult(result); 1658 } catch (RemoteException e) { 1659 Slog.w(TAG, "Failed to call onTimerRecordingResult.", e); 1660 } 1661 } 1662 } 1663 } 1664 1665 void invokeClearTimerRecordingResult(int result) { 1666 synchronized (mLock) { 1667 if (mRecordListenerRecord != null) { 1668 try { 1669 mRecordListenerRecord.mListener.onClearTimerRecordingResult(result); 1670 } catch (RemoteException e) { 1671 Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e); 1672 } 1673 } 1674 } 1675 } 1676 1677 private void invokeCallback(IHdmiControlCallback callback, int result) { 1678 try { 1679 callback.onComplete(result); 1680 } catch (RemoteException e) { 1681 Slog.e(TAG, "Invoking callback failed:" + e); 1682 } 1683 } 1684 1685 private void invokeSystemAudioModeChangeLocked(IHdmiSystemAudioModeChangeListener listener, 1686 boolean enabled) { 1687 try { 1688 listener.onStatusChanged(enabled); 1689 } catch (RemoteException e) { 1690 Slog.e(TAG, "Invoking callback failed:" + e); 1691 } 1692 } 1693 1694 private void announceHotplugEvent(int portId, boolean connected) { 1695 HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected); 1696 synchronized (mLock) { 1697 for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) { 1698 invokeHotplugEventListenerLocked(record.mListener, event); 1699 } 1700 } 1701 } 1702 1703 private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener, 1704 HdmiHotplugEvent event) { 1705 try { 1706 listener.onReceived(event); 1707 } catch (RemoteException e) { 1708 Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e); 1709 } 1710 } 1711 1712 private HdmiCecLocalDeviceTv tv() { 1713 return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV); 1714 } 1715 1716 boolean isTvDevice() { 1717 return tv() != null; 1718 } 1719 1720 private HdmiCecLocalDevicePlayback playback() { 1721 return (HdmiCecLocalDevicePlayback) 1722 mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK); 1723 } 1724 1725 AudioManager getAudioManager() { 1726 return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 1727 } 1728 1729 boolean isControlEnabled() { 1730 synchronized (mLock) { 1731 return mHdmiControlEnabled; 1732 } 1733 } 1734 1735 @ServiceThreadOnly 1736 int getPowerStatus() { 1737 assertRunOnServiceThread(); 1738 return mPowerStatus; 1739 } 1740 1741 @ServiceThreadOnly 1742 boolean isPowerOnOrTransient() { 1743 assertRunOnServiceThread(); 1744 return mPowerStatus == HdmiControlManager.POWER_STATUS_ON 1745 || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; 1746 } 1747 1748 @ServiceThreadOnly 1749 boolean isPowerStandbyOrTransient() { 1750 assertRunOnServiceThread(); 1751 return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY 1752 || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; 1753 } 1754 1755 @ServiceThreadOnly 1756 boolean isPowerStandby() { 1757 assertRunOnServiceThread(); 1758 return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY; 1759 } 1760 1761 @ServiceThreadOnly 1762 void wakeUp() { 1763 assertRunOnServiceThread(); 1764 mWakeUpMessageReceived = true; 1765 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); 1766 pm.wakeUp(SystemClock.uptimeMillis()); 1767 // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets 1768 // the intent, the sequence will continue at onWakeUp(). 1769 } 1770 1771 @ServiceThreadOnly 1772 void standby() { 1773 assertRunOnServiceThread(); 1774 mStandbyMessageReceived = true; 1775 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); 1776 pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0); 1777 // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets 1778 // the intent, the sequence will continue at onStandby(). 1779 } 1780 1781 @ServiceThreadOnly 1782 private void onWakeUp() { 1783 assertRunOnServiceThread(); 1784 mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; 1785 if (mCecController != null) { 1786 if (mHdmiControlEnabled) { 1787 int startReason = INITIATED_BY_SCREEN_ON; 1788 if (mWakeUpMessageReceived) { 1789 startReason = INITIATED_BY_WAKE_UP_MESSAGE; 1790 } 1791 initializeCec(startReason); 1792 } 1793 } else { 1794 Slog.i(TAG, "Device does not support HDMI-CEC."); 1795 } 1796 // TODO: Initialize MHL local devices. 1797 } 1798 1799 @ServiceThreadOnly 1800 private void onStandby() { 1801 assertRunOnServiceThread(); 1802 mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; 1803 1804 final List<HdmiCecLocalDevice> devices = getAllLocalDevices(); 1805 disableDevices(new PendingActionClearedCallback() { 1806 @Override 1807 public void onCleared(HdmiCecLocalDevice device) { 1808 Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType); 1809 devices.remove(device); 1810 if (devices.isEmpty()) { 1811 onStandbyCompleted(); 1812 // We will not clear local devices here, since some OEM/SOC will keep passing 1813 // the received packets until the application processor enters to the sleep 1814 // actually. 1815 } 1816 } 1817 }); 1818 } 1819 1820 @ServiceThreadOnly 1821 private void onLanguageChanged(String language) { 1822 assertRunOnServiceThread(); 1823 mLanguage = language; 1824 1825 if (isTvDevice()) { 1826 tv().broadcastMenuLanguage(language); 1827 } 1828 } 1829 1830 @ServiceThreadOnly 1831 String getLanguage() { 1832 assertRunOnServiceThread(); 1833 return mLanguage; 1834 } 1835 1836 private void disableDevices(PendingActionClearedCallback callback) { 1837 if (mCecController != null) { 1838 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 1839 device.disableDevice(mStandbyMessageReceived, callback); 1840 } 1841 if (isTvDevice()) { 1842 unregisterSettingsObserver(); 1843 } 1844 } 1845 1846 mMhlController.clearAllLocalDevices(); 1847 } 1848 1849 @ServiceThreadOnly 1850 private void clearLocalDevices() { 1851 assertRunOnServiceThread(); 1852 if (mCecController == null) { 1853 return; 1854 } 1855 mCecController.clearLogicalAddress(); 1856 mCecController.clearLocalDevices(); 1857 } 1858 1859 @ServiceThreadOnly 1860 private void onStandbyCompleted() { 1861 assertRunOnServiceThread(); 1862 Slog.v(TAG, "onStandbyCompleted"); 1863 1864 if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) { 1865 return; 1866 } 1867 mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; 1868 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 1869 device.onStandby(mStandbyMessageReceived); 1870 } 1871 mStandbyMessageReceived = false; 1872 mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED); 1873 } 1874 1875 private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) { 1876 VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType); 1877 try { 1878 listener.asBinder().linkToDeath(record, 0); 1879 } catch (RemoteException e) { 1880 Slog.w(TAG, "Listener already died"); 1881 return; 1882 } 1883 synchronized (mLock) { 1884 mVendorCommandListenerRecords.add(record); 1885 } 1886 } 1887 1888 boolean invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params, 1889 boolean hasVendorId) { 1890 synchronized (mLock) { 1891 if (mVendorCommandListenerRecords.isEmpty()) { 1892 return false; 1893 } 1894 for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) { 1895 if (record.mDeviceType != deviceType) { 1896 continue; 1897 } 1898 try { 1899 record.mListener.onReceived(srcAddress, params, hasVendorId); 1900 } catch (RemoteException e) { 1901 Slog.e(TAG, "Failed to notify vendor command reception", e); 1902 } 1903 } 1904 return true; 1905 } 1906 } 1907 1908 private void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) { 1909 HdmiMhlVendorCommandListenerRecord record = 1910 new HdmiMhlVendorCommandListenerRecord(listener); 1911 try { 1912 listener.asBinder().linkToDeath(record, 0); 1913 } catch (RemoteException e) { 1914 Slog.w(TAG, "Listener already died."); 1915 return; 1916 } 1917 1918 synchronized (mLock) { 1919 mMhlVendorCommandListenerRecords.add(record); 1920 } 1921 } 1922 1923 void invokeMhlVendorCommandListeners(int portId, int offest, int length, byte[] data) { 1924 synchronized (mLock) { 1925 for (HdmiMhlVendorCommandListenerRecord record : mMhlVendorCommandListenerRecords) { 1926 try { 1927 record.mListener.onReceived(portId, offest, length, data); 1928 } catch (RemoteException e) { 1929 Slog.e(TAG, "Failed to notify MHL vendor command", e); 1930 } 1931 } 1932 } 1933 } 1934 1935 boolean isProhibitMode() { 1936 synchronized (mLock) { 1937 return mProhibitMode; 1938 } 1939 } 1940 1941 void setProhibitMode(boolean enabled) { 1942 synchronized (mLock) { 1943 mProhibitMode = enabled; 1944 } 1945 } 1946 1947 @ServiceThreadOnly 1948 void setCecOption(int key, int value) { 1949 assertRunOnServiceThread(); 1950 mCecController.setOption(key, value); 1951 } 1952 1953 @ServiceThreadOnly 1954 void setControlEnabled(boolean enabled) { 1955 assertRunOnServiceThread(); 1956 1957 int value = toInt(enabled); 1958 mCecController.setOption(OPTION_CEC_ENABLE, value); 1959 mMhlController.setOption(OPTION_MHL_ENABLE, value); 1960 1961 synchronized (mLock) { 1962 mHdmiControlEnabled = enabled; 1963 } 1964 1965 if (enabled) { 1966 initializeCec(INITIATED_BY_ENABLE_CEC); 1967 } else { 1968 disableDevices(new PendingActionClearedCallback() { 1969 @Override 1970 public void onCleared(HdmiCecLocalDevice device) { 1971 assertRunOnServiceThread(); 1972 clearLocalDevices(); 1973 } 1974 }); 1975 } 1976 } 1977 1978 @ServiceThreadOnly 1979 void setActivePortId(int portId) { 1980 assertRunOnServiceThread(); 1981 mActivePortId = portId; 1982 1983 // Resets last input for MHL, which stays valid only after the MHL device was selected, 1984 // and no further switching is done. 1985 setLastInputForMhl(Constants.INVALID_PORT_ID); 1986 } 1987 1988 @ServiceThreadOnly 1989 void setLastInputForMhl(int portId) { 1990 assertRunOnServiceThread(); 1991 mLastInputMhl = portId; 1992 } 1993 1994 @ServiceThreadOnly 1995 int getLastInputForMhl() { 1996 assertRunOnServiceThread(); 1997 return mLastInputMhl; 1998 } 1999 2000 /** 2001 * Performs input change, routing control for MHL device. 2002 * 2003 * @param portId MHL port, or the last port to go back to if {@code contentOn} is false 2004 * @param contentOn {@code true} if RAP data is content on; otherwise false 2005 */ 2006 @ServiceThreadOnly 2007 void changeInputForMhl(int portId, boolean contentOn) { 2008 assertRunOnServiceThread(); 2009 final int lastInput = contentOn ? tv().getActivePortId() : Constants.INVALID_PORT_ID; 2010 tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() { 2011 @Override 2012 public void onComplete(int result) throws RemoteException { 2013 // Keep the last input to switch back later when RAP[ContentOff] is received. 2014 // This effectively sets the port to invalid one if the switching is for 2015 // RAP[ContentOff]. 2016 setLastInputForMhl(lastInput); 2017 } 2018 }); 2019 2020 // MHL device is always directly connected to the port. Update the active port ID to avoid 2021 // unnecessary post-routing control task. 2022 tv().setActivePortId(portId); 2023 2024 // The port is either the MHL-enabled port where the mobile device is connected, or 2025 // the last port to go back to when turnoff command is received. Note that the last port 2026 // may not be the MHL-enabled one. In this case the device info to be passed to 2027 // input change listener should be the one describing the corresponding HDMI port. 2028 HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId); 2029 HdmiDeviceInfo info = (device != null && device.getInfo() != null) 2030 ? device.getInfo() 2031 : mPortDeviceMap.get(portId); 2032 invokeInputChangeListener(info); 2033 } 2034 2035 void setMhlInputChangeEnabled(boolean enabled) { 2036 mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled)); 2037 2038 synchronized (mLock) { 2039 mMhlInputChangeEnabled = enabled; 2040 } 2041 } 2042 2043 boolean isMhlInputChangeEnabled() { 2044 synchronized (mLock) { 2045 return mMhlInputChangeEnabled; 2046 } 2047 } 2048 2049 @ServiceThreadOnly 2050 void displayOsd(int messageId) { 2051 assertRunOnServiceThread(); 2052 Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE); 2053 intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId); 2054 getContext().sendBroadcastAsUser(intent, UserHandle.ALL, 2055 HdmiControlService.PERMISSION); 2056 } 2057 2058 @ServiceThreadOnly 2059 void displayOsd(int messageId, int extra) { 2060 assertRunOnServiceThread(); 2061 Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE); 2062 intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId); 2063 intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRAM_PARAM1, extra); 2064 getContext().sendBroadcastAsUser(intent, UserHandle.ALL, 2065 HdmiControlService.PERMISSION); 2066 } 2067} 2068