1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.tv; 18 19 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; 20 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY; 21 import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED; 22 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.hardware.hdmi.HdmiControlManager; 28 import android.hardware.hdmi.HdmiDeviceInfo; 29 import android.hardware.hdmi.HdmiHotplugEvent; 30 import android.hardware.hdmi.IHdmiControlService; 31 import android.hardware.hdmi.IHdmiDeviceEventListener; 32 import android.hardware.hdmi.IHdmiHotplugEventListener; 33 import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; 34 import android.media.AudioDevicePort; 35 import android.media.AudioFormat; 36 import android.media.AudioGain; 37 import android.media.AudioGainConfig; 38 import android.media.AudioManager; 39 import android.media.AudioPatch; 40 import android.media.AudioPort; 41 import android.media.AudioPortConfig; 42 import android.media.AudioSystem; 43 import android.media.tv.ITvInputHardware; 44 import android.media.tv.ITvInputHardwareCallback; 45 import android.media.tv.TvInputHardwareInfo; 46 import android.media.tv.TvInputInfo; 47 import android.media.tv.TvInputService.PriorityHintUseCaseType; 48 import android.media.tv.TvStreamConfig; 49 import android.media.tv.tunerresourcemanager.ResourceClientProfile; 50 import android.media.tv.tunerresourcemanager.TunerResourceManager; 51 import android.os.Handler; 52 import android.os.IBinder; 53 import android.os.Message; 54 import android.os.RemoteException; 55 import android.os.ServiceManager; 56 import android.util.ArrayMap; 57 import android.util.Slog; 58 import android.util.SparseArray; 59 import android.util.SparseBooleanArray; 60 import android.view.Surface; 61 62 import com.android.internal.util.DumpUtils; 63 import com.android.internal.util.IndentingPrintWriter; 64 import com.android.server.SystemService; 65 66 import java.io.FileDescriptor; 67 import java.io.PrintWriter; 68 import java.util.ArrayList; 69 import java.util.Arrays; 70 import java.util.Collections; 71 import java.util.Iterator; 72 import java.util.LinkedList; 73 import java.util.List; 74 import java.util.Map; 75 76 /** 77 * A helper class for TvInputManagerService to handle TV input hardware. 78 * 79 * This class does a basic connection management and forwarding calls to TvInputHal which eventually 80 * calls to tv_input HAL module. 81 * 82 * @hide 83 */ 84 class TvInputHardwareManager implements TvInputHal.Callback { 85 private static final String TAG = TvInputHardwareManager.class.getSimpleName(); 86 87 private final Context mContext; 88 private final Listener mListener; 89 private final TvInputHal mHal = new TvInputHal(this); 90 private final SparseArray<Connection> mConnections = new SparseArray<>(); 91 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>(); 92 private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>(); 93 /* A map from a device ID to the matching TV input ID. */ 94 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>(); 95 /* A map from a HDMI logical address to the matching TV input ID. */ 96 private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>(); 97 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>(); 98 99 private final AudioManager mAudioManager; 100 private final IHdmiHotplugEventListener mHdmiHotplugEventListener = 101 new HdmiHotplugEventListener(); 102 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener(); 103 private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener = 104 new HdmiSystemAudioModeChangeListener(); 105 private final BroadcastReceiver mVolumeReceiver = new BroadcastReceiver() { 106 @Override 107 public void onReceive(Context context, Intent intent) { 108 handleVolumeChange(context, intent); 109 } 110 }; 111 private int mCurrentIndex = 0; 112 private int mCurrentMaxIndex = 0; 113 114 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray(); 115 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>(); 116 117 private final List<Message> mPendingTvinputInfoEvents = new LinkedList<>(); 118 119 // Calls to mListener should happen here. 120 private final Handler mHandler = new ListenerHandler(); 121 122 private final Object mLock = new Object(); 123 TvInputHardwareManager(Context context, Listener listener)124 public TvInputHardwareManager(Context context, Listener listener) { 125 mContext = context; 126 mListener = listener; 127 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 128 mHal.init(); 129 } 130 onBootPhase(int phase)131 public void onBootPhase(int phase) { 132 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 133 IHdmiControlService hdmiControlService = IHdmiControlService.Stub.asInterface( 134 ServiceManager.getService(Context.HDMI_CONTROL_SERVICE)); 135 if (hdmiControlService != null) { 136 try { 137 hdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener); 138 hdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener); 139 hdmiControlService.addSystemAudioModeChangeListener( 140 mHdmiSystemAudioModeChangeListener); 141 mHdmiDeviceList.addAll(hdmiControlService.getInputDevices()); 142 } catch (RemoteException e) { 143 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e); 144 } 145 } else { 146 Slog.w(TAG, "HdmiControlService is not available"); 147 } 148 final IntentFilter filter = new IntentFilter(); 149 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); 150 filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION); 151 mContext.registerReceiver(mVolumeReceiver, filter); 152 updateVolume(); 153 } 154 } 155 156 @Override onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs)157 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) { 158 synchronized (mLock) { 159 Connection connection = new Connection(info); 160 connection.updateConfigsLocked(configs); 161 connection.updateCableConnectionStatusLocked(info.getCableConnectionStatus()); 162 mConnections.put(info.getDeviceId(), connection); 163 buildHardwareListLocked(); 164 mHandler.obtainMessage( 165 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget(); 166 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) { 167 processPendingHdmiDeviceEventsLocked(); 168 } 169 } 170 } 171 buildHardwareListLocked()172 private void buildHardwareListLocked() { 173 mHardwareList.clear(); 174 for (int i = 0; i < mConnections.size(); ++i) { 175 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked()); 176 } 177 } 178 179 @Override onDeviceUnavailable(int deviceId)180 public void onDeviceUnavailable(int deviceId) { 181 synchronized (mLock) { 182 Connection connection = mConnections.get(deviceId); 183 if (connection == null) { 184 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId); 185 return; 186 } 187 connection.resetLocked(null, null, null, null, null, null); 188 mConnections.remove(deviceId); 189 buildHardwareListLocked(); 190 TvInputHardwareInfo info = connection.getHardwareInfoLocked(); 191 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) { 192 // Remove HDMI devices linked with this hardware. 193 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) { 194 HdmiDeviceInfo deviceInfo = it.next(); 195 if (deviceInfo.getPortId() == info.getHdmiPortId()) { 196 mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0, 197 deviceInfo).sendToTarget(); 198 it.remove(); 199 } 200 } 201 } 202 mHandler.obtainMessage( 203 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget(); 204 } 205 } 206 207 @Override onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs, int cableConnectionStatus)208 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs, 209 int cableConnectionStatus) { 210 synchronized (mLock) { 211 Connection connection = mConnections.get(deviceId); 212 if (connection == null) { 213 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with " 214 + deviceId); 215 return; 216 } 217 int previousConfigsLength = connection.getConfigsLengthLocked(); 218 int previousCableConnectionStatus = connection.getInputStateLocked(); 219 connection.updateConfigsLocked(configs); 220 String inputId = mHardwareInputIdMap.get(deviceId); 221 if (inputId != null) { 222 if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) { 223 if (previousCableConnectionStatus != connection.getInputStateLocked()) { 224 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, 225 connection.getInputStateLocked(), 0, inputId).sendToTarget(); 226 } 227 } else { 228 if ((previousConfigsLength == 0) 229 != (connection.getConfigsLengthLocked() == 0)) { 230 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, 231 connection.getInputStateLocked(), 0, inputId).sendToTarget(); 232 } 233 } 234 } else { 235 Message msg = mHandler.obtainMessage(ListenerHandler.TVINPUT_INFO_ADDED, 236 deviceId, cableConnectionStatus, connection); 237 for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext();) { 238 if (it.next().arg1 == deviceId) { 239 it.remove(); 240 } 241 } 242 mPendingTvinputInfoEvents.add(msg); 243 } 244 ITvInputHardwareCallback callback = connection.getCallbackLocked(); 245 if (callback != null) { 246 try { 247 callback.onStreamConfigChanged(configs); 248 } catch (RemoteException e) { 249 Slog.e(TAG, "error in onStreamConfigurationChanged", e); 250 } 251 } 252 } 253 } 254 255 @Override onFirstFrameCaptured(int deviceId, int streamId)256 public void onFirstFrameCaptured(int deviceId, int streamId) { 257 synchronized (mLock) { 258 Connection connection = mConnections.get(deviceId); 259 if (connection == null) { 260 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with " 261 + deviceId); 262 return; 263 } 264 Runnable runnable = connection.getOnFirstFrameCapturedLocked(); 265 if (runnable != null) { 266 runnable.run(); 267 connection.setOnFirstFrameCapturedLocked(null); 268 } 269 } 270 } 271 getHardwareList()272 public List<TvInputHardwareInfo> getHardwareList() { 273 synchronized (mLock) { 274 return Collections.unmodifiableList(mHardwareList); 275 } 276 } 277 getHdmiDeviceList()278 public List<HdmiDeviceInfo> getHdmiDeviceList() { 279 synchronized (mLock) { 280 return Collections.unmodifiableList(mHdmiDeviceList); 281 } 282 } 283 checkUidChangedLocked( Connection connection, int callingUid, int resolvedUserId)284 private boolean checkUidChangedLocked( 285 Connection connection, int callingUid, int resolvedUserId) { 286 Integer connectionCallingUid = connection.getCallingUidLocked(); 287 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked(); 288 return connectionCallingUid == null || connectionResolvedUserId == null 289 || connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId; 290 } 291 addHardwareInput(int deviceId, TvInputInfo info)292 public void addHardwareInput(int deviceId, TvInputInfo info) { 293 synchronized (mLock) { 294 String oldInputId = mHardwareInputIdMap.get(deviceId); 295 if (oldInputId != null) { 296 Slog.w(TAG, "Trying to override previous registration: old = " 297 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = " 298 + info + ":" + deviceId); 299 } 300 mHardwareInputIdMap.put(deviceId, info.getId()); 301 mInputMap.put(info.getId(), info); 302 processPendingTvInputInfoEventsLocked(); 303 Slog.d(TAG,"deviceId ="+ deviceId+", tvinputinfo = "+info); 304 305 // Process pending state changes 306 307 // For logical HDMI devices, they have information from HDMI CEC signals. 308 for (int i = 0; i < mHdmiStateMap.size(); ++i) { 309 TvInputHardwareInfo hardwareInfo = 310 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i)); 311 if (hardwareInfo == null) { 312 continue; 313 } 314 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId()); 315 if (inputId != null && inputId.equals(info.getId())) { 316 // No HDMI hotplug does not necessarily mean disconnected, as old devices may 317 // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to 318 // denote unknown state. 319 int state = mHdmiStateMap.valueAt(i) 320 ? INPUT_STATE_CONNECTED 321 : INPUT_STATE_CONNECTED_STANDBY; 322 mHandler.obtainMessage( 323 ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget(); 324 return; 325 } 326 } 327 // For the rest of the devices, we can tell by the cable connection status. 328 Connection connection = mConnections.get(deviceId); 329 if (connection != null) { 330 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, 331 connection.getInputStateLocked(), 0, info.getId()).sendToTarget(); 332 } 333 } 334 } 335 indexOfEqualValue(SparseArray<T> map, T value)336 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) { 337 for (int i = 0; i < map.size(); ++i) { 338 if (map.valueAt(i).equals(value)) { 339 return i; 340 } 341 } 342 return -1; 343 } 344 intArrayContains(int[] array, int value)345 private static boolean intArrayContains(int[] array, int value) { 346 for (int element : array) { 347 if (element == value) return true; 348 } 349 return false; 350 } 351 addHdmiInput(int id, TvInputInfo info)352 public void addHdmiInput(int id, TvInputInfo info) { 353 if (info.getType() != TvInputInfo.TYPE_HDMI) { 354 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type."); 355 } 356 synchronized (mLock) { 357 String parentId = info.getParentId(); 358 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId); 359 if (parentIndex < 0) { 360 throw new IllegalArgumentException("info (" + info + ") has invalid parentId."); 361 } 362 String oldInputId = mHdmiInputIdMap.get(id); 363 if (oldInputId != null) { 364 Slog.w(TAG, "Trying to override previous registration: old = " 365 + mInputMap.get(oldInputId) + ":" + id + ", new = " 366 + info + ":" + id); 367 } 368 mHdmiInputIdMap.put(id, info.getId()); 369 mInputMap.put(info.getId(), info); 370 } 371 } 372 removeHardwareInput(String inputId)373 public void removeHardwareInput(String inputId) { 374 synchronized (mLock) { 375 mInputMap.remove(inputId); 376 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId); 377 if (hardwareIndex >= 0) { 378 mHardwareInputIdMap.removeAt(hardwareIndex); 379 } 380 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId); 381 if (deviceIndex >= 0) { 382 mHdmiInputIdMap.removeAt(deviceIndex); 383 } 384 } 385 } 386 387 /** 388 * Create a TvInputHardware object with a specific deviceId. One service at a time can access 389 * the object, and if more than one process attempts to create hardware with the same deviceId, 390 * the latest service will get the object and all the other hardware are released. The 391 * release is notified via ITvInputHardwareCallback.onReleased(). 392 */ acquireHardware(int deviceId, ITvInputHardwareCallback callback, TvInputInfo info, int callingUid, int resolvedUserId, String tvInputSessionId, @PriorityHintUseCaseType int priorityHint)393 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback, 394 TvInputInfo info, int callingUid, int resolvedUserId, 395 String tvInputSessionId, @PriorityHintUseCaseType int priorityHint) { 396 if (callback == null) { 397 throw new NullPointerException(); 398 } 399 TunerResourceManager trm = (TunerResourceManager) mContext.getSystemService( 400 Context.TV_TUNER_RESOURCE_MGR_SERVICE); 401 synchronized (mLock) { 402 Connection connection = mConnections.get(deviceId); 403 if (connection == null) { 404 Slog.e(TAG, "Invalid deviceId : " + deviceId); 405 return null; 406 } 407 408 ResourceClientProfile profile = new ResourceClientProfile(); 409 profile.tvInputSessionId = tvInputSessionId; 410 profile.useCase = priorityHint; 411 ResourceClientProfile holderProfile = connection.getResourceClientProfileLocked(); 412 if (holderProfile != null && trm != null 413 && !trm.isHigherPriority(profile, holderProfile)) { 414 Slog.d(TAG, "Acquiring does not show higher priority than the current holder." 415 + " Device id:" + deviceId); 416 return null; 417 } 418 TvInputHardwareImpl hardware = 419 new TvInputHardwareImpl(connection.getHardwareInfoLocked()); 420 try { 421 callback.asBinder().linkToDeath(connection, 0); 422 } catch (RemoteException e) { 423 hardware.release(); 424 return null; 425 } 426 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId, 427 profile); 428 return connection.getHardwareLocked(); 429 } 430 } 431 432 /** 433 * Release the specified hardware. 434 */ releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid, int resolvedUserId)435 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid, 436 int resolvedUserId) { 437 synchronized (mLock) { 438 Connection connection = mConnections.get(deviceId); 439 if (connection == null) { 440 Slog.e(TAG, "Invalid deviceId : " + deviceId); 441 return; 442 } 443 if (connection.getHardwareLocked() != hardware 444 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) { 445 return; 446 } 447 ITvInputHardwareCallback callback = connection.getCallbackLocked(); 448 if (callback != null) { 449 callback.asBinder().unlinkToDeath(connection, 0); 450 } 451 connection.resetLocked(null, null, null, null, null, null); 452 } 453 } 454 findHardwareInfoForHdmiPortLocked(int port)455 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) { 456 for (TvInputHardwareInfo hardwareInfo : mHardwareList) { 457 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI 458 && hardwareInfo.getHdmiPortId() == port) { 459 return hardwareInfo; 460 } 461 } 462 return null; 463 } 464 findDeviceIdForInputIdLocked(String inputId)465 private int findDeviceIdForInputIdLocked(String inputId) { 466 for (int i = 0; i < mConnections.size(); ++i) { 467 Connection connection = mConnections.get(i); 468 if (connection.getInfoLocked().getId().equals(inputId)) { 469 return i; 470 } 471 } 472 return -1; 473 } 474 475 /** 476 * Get the list of TvStreamConfig which is buffered mode. 477 */ getAvailableTvStreamConfigList(String inputId, int callingUid, int resolvedUserId)478 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid, 479 int resolvedUserId) { 480 List<TvStreamConfig> configsList = new ArrayList<>(); 481 synchronized (mLock) { 482 int deviceId = findDeviceIdForInputIdLocked(inputId); 483 if (deviceId < 0) { 484 Slog.e(TAG, "Invalid inputId : " + inputId); 485 return configsList; 486 } 487 Connection connection = mConnections.get(deviceId); 488 for (TvStreamConfig config : connection.getConfigsLocked()) { 489 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) { 490 configsList.add(config); 491 } 492 } 493 } 494 return configsList; 495 } 496 497 /** 498 * Take a snapshot of the given TV input into the provided Surface. 499 */ captureFrame(String inputId, Surface surface, final TvStreamConfig config, int callingUid, int resolvedUserId)500 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config, 501 int callingUid, int resolvedUserId) { 502 synchronized (mLock) { 503 int deviceId = findDeviceIdForInputIdLocked(inputId); 504 if (deviceId < 0) { 505 Slog.e(TAG, "Invalid inputId : " + inputId); 506 return false; 507 } 508 Connection connection = mConnections.get(deviceId); 509 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked(); 510 if (hardwareImpl != null) { 511 // Stop previous capture. 512 Runnable runnable = connection.getOnFirstFrameCapturedLocked(); 513 if (runnable != null) { 514 runnable.run(); 515 connection.setOnFirstFrameCapturedLocked(null); 516 } 517 518 boolean result = hardwareImpl.startCapture(surface, config); 519 if (result) { 520 connection.setOnFirstFrameCapturedLocked(new Runnable() { 521 @Override 522 public void run() { 523 hardwareImpl.stopCapture(config); 524 } 525 }); 526 } 527 return result; 528 } 529 } 530 return false; 531 } 532 processPendingHdmiDeviceEventsLocked()533 private void processPendingHdmiDeviceEventsLocked() { 534 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) { 535 Message msg = it.next(); 536 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj; 537 TvInputHardwareInfo hardwareInfo = 538 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()); 539 if (hardwareInfo != null) { 540 msg.sendToTarget(); 541 it.remove(); 542 } 543 } 544 } 545 546 processPendingTvInputInfoEventsLocked()547 private void processPendingTvInputInfoEventsLocked() { 548 for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext(); ) { 549 Message msg = it.next(); 550 int deviceId = msg.arg1; 551 String inputId = mHardwareInputIdMap.get(deviceId); 552 if (inputId != null) { 553 msg.sendToTarget(); 554 it.remove(); 555 } 556 } 557 } 558 559 updateVolume()560 private void updateVolume() { 561 mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 562 mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 563 } 564 handleVolumeChange(Context context, Intent intent)565 private void handleVolumeChange(Context context, Intent intent) { 566 String action = intent.getAction(); 567 switch (action) { 568 case AudioManager.VOLUME_CHANGED_ACTION: { 569 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 570 if (streamType != AudioManager.STREAM_MUSIC) { 571 return; 572 } 573 int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 574 if (index == mCurrentIndex) { 575 return; 576 } 577 mCurrentIndex = index; 578 break; 579 } 580 case AudioManager.STREAM_MUTE_CHANGED_ACTION: { 581 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 582 if (streamType != AudioManager.STREAM_MUSIC) { 583 return; 584 } 585 // volume index will be updated at onMediaStreamVolumeChanged() through 586 // updateVolume(). 587 break; 588 } 589 default: 590 Slog.w(TAG, "Unrecognized intent: " + intent); 591 return; 592 } 593 synchronized (mLock) { 594 for (int i = 0; i < mConnections.size(); ++i) { 595 TvInputHardwareImpl hardwareImpl = mConnections.valueAt(i).getHardwareImplLocked(); 596 if (hardwareImpl != null) { 597 hardwareImpl.onMediaStreamVolumeChanged(); 598 } 599 } 600 } 601 } 602 getMediaStreamVolume()603 private float getMediaStreamVolume() { 604 return (float) mCurrentIndex / (float) mCurrentMaxIndex; 605 } 606 dump(FileDescriptor fd, final PrintWriter writer, String[] args)607 public void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 608 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 609 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 610 611 synchronized (mLock) { 612 pw.println("TvInputHardwareManager Info:"); 613 pw.increaseIndent(); 614 pw.println("mConnections: deviceId -> Connection"); 615 pw.increaseIndent(); 616 for (int i = 0; i < mConnections.size(); i++) { 617 int deviceId = mConnections.keyAt(i); 618 Connection mConnection = mConnections.valueAt(i); 619 pw.println(deviceId + ": " + mConnection); 620 621 } 622 pw.decreaseIndent(); 623 624 pw.println("mHardwareList:"); 625 pw.increaseIndent(); 626 for (TvInputHardwareInfo tvInputHardwareInfo : mHardwareList) { 627 pw.println(tvInputHardwareInfo); 628 } 629 pw.decreaseIndent(); 630 631 pw.println("mHdmiDeviceList:"); 632 pw.increaseIndent(); 633 for (HdmiDeviceInfo hdmiDeviceInfo : mHdmiDeviceList) { 634 pw.println(hdmiDeviceInfo); 635 } 636 pw.decreaseIndent(); 637 638 pw.println("mHardwareInputIdMap: deviceId -> inputId"); 639 pw.increaseIndent(); 640 for (int i = 0 ; i < mHardwareInputIdMap.size(); i++) { 641 int deviceId = mHardwareInputIdMap.keyAt(i); 642 String inputId = mHardwareInputIdMap.valueAt(i); 643 pw.println(deviceId + ": " + inputId); 644 } 645 pw.decreaseIndent(); 646 647 pw.println("mHdmiInputIdMap: id -> inputId"); 648 pw.increaseIndent(); 649 for (int i = 0; i < mHdmiInputIdMap.size(); i++) { 650 int id = mHdmiInputIdMap.keyAt(i); 651 String inputId = mHdmiInputIdMap.valueAt(i); 652 pw.println(id + ": " + inputId); 653 } 654 pw.decreaseIndent(); 655 656 pw.println("mInputMap: inputId -> inputInfo"); 657 pw.increaseIndent(); 658 for(Map.Entry<String, TvInputInfo> entry : mInputMap.entrySet()) { 659 pw.println(entry.getKey() + ": " + entry.getValue()); 660 } 661 pw.decreaseIndent(); 662 pw.decreaseIndent(); 663 } 664 } 665 666 private class Connection implements IBinder.DeathRecipient { 667 private TvInputHardwareInfo mHardwareInfo; 668 private TvInputInfo mInfo; 669 private TvInputHardwareImpl mHardware = null; 670 private ITvInputHardwareCallback mCallback; 671 private TvStreamConfig[] mConfigs = null; 672 private Integer mCallingUid = null; 673 private Integer mResolvedUserId = null; 674 private Runnable mOnFirstFrameCaptured; 675 private ResourceClientProfile mResourceClientProfile = null; 676 private boolean mIsCableConnectionStatusUpdated = false; 677 Connection(TvInputHardwareInfo hardwareInfo)678 public Connection(TvInputHardwareInfo hardwareInfo) { 679 mHardwareInfo = hardwareInfo; 680 } 681 682 // *Locked methods assume TvInputHardwareManager.mLock is held. 683 resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback, TvInputInfo info, Integer callingUid, Integer resolvedUserId, ResourceClientProfile profile)684 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback, 685 TvInputInfo info, Integer callingUid, Integer resolvedUserId, 686 ResourceClientProfile profile) { 687 if (mHardware != null) { 688 try { 689 mCallback.onReleased(); 690 } catch (RemoteException e) { 691 Slog.e(TAG, "error in Connection::resetLocked", e); 692 } 693 mHardware.release(); 694 } 695 mHardware = hardware; 696 mCallback = callback; 697 mInfo = info; 698 mCallingUid = callingUid; 699 mResolvedUserId = resolvedUserId; 700 mOnFirstFrameCaptured = null; 701 mResourceClientProfile = profile; 702 703 if (mHardware != null && mCallback != null) { 704 try { 705 mCallback.onStreamConfigChanged(getConfigsLocked()); 706 } catch (RemoteException e) { 707 Slog.e(TAG, "error in Connection::resetLocked", e); 708 } 709 } 710 } 711 updateConfigsLocked(TvStreamConfig[] configs)712 public void updateConfigsLocked(TvStreamConfig[] configs) { 713 mConfigs = configs; 714 } 715 getHardwareInfoLocked()716 public TvInputHardwareInfo getHardwareInfoLocked() { 717 return mHardwareInfo; 718 } 719 getInfoLocked()720 public TvInputInfo getInfoLocked() { 721 return mInfo; 722 } 723 getHardwareLocked()724 public ITvInputHardware getHardwareLocked() { 725 return mHardware; 726 } 727 getHardwareImplLocked()728 public TvInputHardwareImpl getHardwareImplLocked() { 729 return mHardware; 730 } 731 getCallbackLocked()732 public ITvInputHardwareCallback getCallbackLocked() { 733 return mCallback; 734 } 735 getConfigsLocked()736 public TvStreamConfig[] getConfigsLocked() { 737 return mConfigs; 738 } 739 getCallingUidLocked()740 public Integer getCallingUidLocked() { 741 return mCallingUid; 742 } 743 getResolvedUserIdLocked()744 public Integer getResolvedUserIdLocked() { 745 return mResolvedUserId; 746 } 747 setOnFirstFrameCapturedLocked(Runnable runnable)748 public void setOnFirstFrameCapturedLocked(Runnable runnable) { 749 mOnFirstFrameCaptured = runnable; 750 } 751 getOnFirstFrameCapturedLocked()752 public Runnable getOnFirstFrameCapturedLocked() { 753 return mOnFirstFrameCaptured; 754 } 755 getResourceClientProfileLocked()756 public ResourceClientProfile getResourceClientProfileLocked() { 757 return mResourceClientProfile; 758 } 759 760 @Override binderDied()761 public void binderDied() { 762 synchronized (mLock) { 763 resetLocked(null, null, null, null, null, null); 764 } 765 } 766 toString()767 public String toString() { 768 return "Connection{" 769 + " mHardwareInfo: " + mHardwareInfo 770 + ", mInfo: " + mInfo 771 + ", mCallback: " + mCallback 772 + ", mConfigs: " + Arrays.toString(mConfigs) 773 + ", mCallingUid: " + mCallingUid 774 + ", mResolvedUserId: " + mResolvedUserId 775 + ", mResourceClientProfile: " + mResourceClientProfile 776 + " }"; 777 } 778 updateCableConnectionStatusLocked(int cableConnectionStatus)779 public boolean updateCableConnectionStatusLocked(int cableConnectionStatus) { 780 // Update connection status only if it's not default value 781 if (cableConnectionStatus != TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN 782 || mIsCableConnectionStatusUpdated) { 783 mIsCableConnectionStatusUpdated = true; 784 mHardwareInfo = mHardwareInfo.toBuilder() 785 .cableConnectionStatus(cableConnectionStatus).build(); 786 } 787 return mIsCableConnectionStatusUpdated; 788 } 789 getConfigsLengthLocked()790 private int getConfigsLengthLocked() { 791 return mConfigs == null ? 0 : mConfigs.length; 792 } 793 getInputStateLocked()794 private int getInputStateLocked() { 795 int configsLength = getConfigsLengthLocked(); 796 if (configsLength > 0) { 797 if (!mIsCableConnectionStatusUpdated) { 798 return INPUT_STATE_CONNECTED; 799 } 800 } 801 switch (mHardwareInfo.getCableConnectionStatus()) { 802 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_CONNECTED: 803 return INPUT_STATE_CONNECTED; 804 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_DISCONNECTED: 805 return INPUT_STATE_DISCONNECTED; 806 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN: 807 default: 808 return INPUT_STATE_CONNECTED_STANDBY; 809 } 810 } 811 } 812 813 private class TvInputHardwareImpl extends ITvInputHardware.Stub { 814 private final TvInputHardwareInfo mInfo; 815 private boolean mReleased = false; 816 private final Object mImplLock = new Object(); 817 818 private final AudioManager.OnAudioPortUpdateListener mAudioListener = 819 new AudioManager.OnAudioPortUpdateListener() { 820 @Override 821 public void onAudioPortListUpdate(AudioPort[] portList) { 822 synchronized (mImplLock) { 823 updateAudioConfigLocked(); 824 } 825 } 826 827 @Override 828 public void onAudioPatchListUpdate(AudioPatch[] patchList) { 829 // No-op 830 } 831 832 @Override 833 public void onServiceDied() { 834 synchronized (mImplLock) { 835 mAudioSource = null; 836 mAudioSink.clear(); 837 if (mAudioPatch != null) { 838 mAudioManager.releaseAudioPatch(mAudioPatch); 839 mAudioPatch = null; 840 } 841 } 842 } 843 }; 844 private int mOverrideAudioType = AudioManager.DEVICE_NONE; 845 private String mOverrideAudioAddress = ""; 846 private AudioDevicePort mAudioSource; 847 private List<AudioDevicePort> mAudioSink = new ArrayList<>(); 848 private AudioPatch mAudioPatch = null; 849 // Set to an invalid value for a volume, so that current volume can be applied at the 850 // first call to updateAudioConfigLocked(). 851 private float mCommittedVolume = -1f; 852 private float mSourceVolume = 0.0f; 853 854 private TvStreamConfig mActiveConfig = null; 855 856 private int mDesiredSamplingRate = 0; 857 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT; 858 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT; 859 TvInputHardwareImpl(TvInputHardwareInfo info)860 public TvInputHardwareImpl(TvInputHardwareInfo info) { 861 mInfo = info; 862 mAudioManager.registerAudioPortUpdateListener(mAudioListener); 863 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) { 864 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress()); 865 findAudioSinkFromAudioPolicy(mAudioSink); 866 } 867 } 868 findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks)869 private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) { 870 sinks.clear(); 871 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>(); 872 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) { 873 return; 874 } 875 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); 876 for (AudioDevicePort port : devicePorts) { 877 if ((port.type() & sinkDevice) != 0 && 878 (port.type() & AudioSystem.DEVICE_BIT_IN) == 0) { 879 sinks.add(port); 880 } 881 } 882 } 883 findAudioDevicePort(int type, String address)884 private AudioDevicePort findAudioDevicePort(int type, String address) { 885 if (type == AudioManager.DEVICE_NONE) { 886 return null; 887 } 888 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>(); 889 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) { 890 return null; 891 } 892 for (AudioDevicePort port : devicePorts) { 893 if (port.type() == type && port.address().equals(address)) { 894 return port; 895 } 896 } 897 return null; 898 } 899 release()900 public void release() { 901 synchronized (mImplLock) { 902 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener); 903 if (mAudioPatch != null) { 904 mAudioManager.releaseAudioPatch(mAudioPatch); 905 mAudioPatch = null; 906 } 907 mReleased = true; 908 } 909 } 910 911 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client 912 // attempts to call setSurface with different TvStreamConfig objects, the last call will 913 // prevail. 914 @Override setSurface(Surface surface, TvStreamConfig config)915 public boolean setSurface(Surface surface, TvStreamConfig config) 916 throws RemoteException { 917 synchronized (mImplLock) { 918 if (mReleased) { 919 throw new IllegalStateException("Device already released."); 920 } 921 922 int result = TvInputHal.SUCCESS; 923 if (surface == null) { 924 // The value of config is ignored when surface == null. 925 if (mActiveConfig != null) { 926 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig); 927 mActiveConfig = null; 928 } else { 929 // We already have no active stream. 930 return true; 931 } 932 } else { 933 // It's impossible to set a non-null surface with a null config. 934 if (config == null) { 935 return false; 936 } 937 // Remove stream only if we have an existing active configuration. 938 if (mActiveConfig != null && !config.equals(mActiveConfig)) { 939 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig); 940 if (result != TvInputHal.SUCCESS) { 941 mActiveConfig = null; 942 } 943 } 944 // Proceed only if all previous operations succeeded. 945 if (result == TvInputHal.SUCCESS) { 946 result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config); 947 if (result == TvInputHal.SUCCESS) { 948 mActiveConfig = config; 949 } 950 } 951 } 952 updateAudioConfigLocked(); 953 return result == TvInputHal.SUCCESS; 954 } 955 } 956 957 /** 958 * Update audio configuration (source, sink, patch) all up to current state. 959 */ updateAudioConfigLocked()960 private void updateAudioConfigLocked() { 961 boolean sinkUpdated = updateAudioSinkLocked(); 962 boolean sourceUpdated = updateAudioSourceLocked(); 963 // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here 964 // because Java won't evaluate the latter if the former is true. 965 966 if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) { 967 if (mAudioPatch != null) { 968 mAudioManager.releaseAudioPatch(mAudioPatch); 969 mAudioPatch = null; 970 } 971 return; 972 } 973 974 updateVolume(); 975 float volume = mSourceVolume * getMediaStreamVolume(); 976 AudioGainConfig sourceGainConfig = null; 977 if (mAudioSource.gains().length > 0 && volume != mCommittedVolume) { 978 AudioGain sourceGain = null; 979 for (AudioGain gain : mAudioSource.gains()) { 980 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) { 981 sourceGain = gain; 982 break; 983 } 984 } 985 // NOTE: we only change the source gain in MODE_JOINT here. 986 if (sourceGain != null) { 987 int steps = (sourceGain.maxValue() - sourceGain.minValue()) 988 / sourceGain.stepValue(); 989 int gainValue = sourceGain.minValue(); 990 if (volume < 1.0f) { 991 gainValue += sourceGain.stepValue() * (int) (volume * steps + 0.5); 992 } else { 993 gainValue = sourceGain.maxValue(); 994 } 995 // size of gain values is 1 in MODE_JOINT 996 int[] gainValues = new int[] { gainValue }; 997 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT, 998 sourceGain.channelMask(), gainValues, 0); 999 } else { 1000 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists."); 1001 } 1002 } 1003 1004 AudioPortConfig sourceConfig = mAudioSource.activeConfig(); 1005 List<AudioPortConfig> sinkConfigs = new ArrayList<>(); 1006 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch }; 1007 boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated || mAudioPatch == null; 1008 1009 for (AudioDevicePort audioSink : mAudioSink) { 1010 AudioPortConfig sinkConfig = audioSink.activeConfig(); 1011 int sinkSamplingRate = mDesiredSamplingRate; 1012 int sinkChannelMask = mDesiredChannelMask; 1013 int sinkFormat = mDesiredFormat; 1014 // If sinkConfig != null and values are set to default, 1015 // fill in the sinkConfig values. 1016 if (sinkConfig != null) { 1017 if (sinkSamplingRate == 0) { 1018 sinkSamplingRate = sinkConfig.samplingRate(); 1019 } 1020 if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) { 1021 sinkChannelMask = sinkConfig.channelMask(); 1022 } 1023 if (sinkFormat == AudioFormat.ENCODING_DEFAULT) { 1024 sinkFormat = sinkConfig.format(); 1025 } 1026 } 1027 1028 if (sinkConfig == null 1029 || sinkConfig.samplingRate() != sinkSamplingRate 1030 || sinkConfig.channelMask() != sinkChannelMask 1031 || sinkConfig.format() != sinkFormat) { 1032 // Check for compatibility and reset to default if necessary. 1033 if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate) 1034 && audioSink.samplingRates().length > 0) { 1035 sinkSamplingRate = audioSink.samplingRates()[0]; 1036 } 1037 if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) { 1038 sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT; 1039 } 1040 if (!intArrayContains(audioSink.formats(), sinkFormat)) { 1041 sinkFormat = AudioFormat.ENCODING_DEFAULT; 1042 } 1043 sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask, 1044 sinkFormat, null); 1045 shouldRecreateAudioPatch = true; 1046 } 1047 sinkConfigs.add(sinkConfig); 1048 } 1049 // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be 1050 // non-empty at the beginning of this method. 1051 AudioPortConfig sinkConfig = sinkConfigs.get(0); 1052 if (sourceConfig == null || sourceGainConfig != null) { 1053 int sourceSamplingRate = 0; 1054 if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) { 1055 sourceSamplingRate = sinkConfig.samplingRate(); 1056 } else if (mAudioSource.samplingRates().length > 0) { 1057 // Use any sampling rate and hope audio patch can handle resampling... 1058 sourceSamplingRate = mAudioSource.samplingRates()[0]; 1059 } 1060 int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT; 1061 for (int inChannelMask : mAudioSource.channelMasks()) { 1062 if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask()) 1063 == AudioFormat.channelCountFromInChannelMask(inChannelMask)) { 1064 sourceChannelMask = inChannelMask; 1065 break; 1066 } 1067 } 1068 int sourceFormat = AudioFormat.ENCODING_DEFAULT; 1069 if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) { 1070 sourceFormat = sinkConfig.format(); 1071 } 1072 sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask, 1073 sourceFormat, sourceGainConfig); 1074 shouldRecreateAudioPatch = true; 1075 } 1076 if (shouldRecreateAudioPatch) { 1077 mCommittedVolume = volume; 1078 // only recreate if something was updated or audioPath is null 1079 if (mAudioPatch == null || sinkUpdated ||sourceUpdated ) { 1080 if (mAudioPatch != null) { 1081 mAudioManager.releaseAudioPatch(mAudioPatch); 1082 audioPatchArray[0] = null; 1083 } 1084 mAudioManager.createAudioPatch( 1085 audioPatchArray, 1086 new AudioPortConfig[] { sourceConfig }, 1087 sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()])); 1088 mAudioPatch = audioPatchArray[0]; 1089 } 1090 } 1091 1092 if (sourceGainConfig != null) { 1093 mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig); 1094 } 1095 } 1096 1097 @Override setStreamVolume(float volume)1098 public void setStreamVolume(float volume) throws RemoteException { 1099 synchronized (mImplLock) { 1100 if (mReleased) { 1101 throw new IllegalStateException("Device already released."); 1102 } 1103 mSourceVolume = volume; 1104 updateAudioConfigLocked(); 1105 } 1106 } 1107 startCapture(Surface surface, TvStreamConfig config)1108 private boolean startCapture(Surface surface, TvStreamConfig config) { 1109 synchronized (mImplLock) { 1110 if (mReleased) { 1111 return false; 1112 } 1113 if (surface == null || config == null) { 1114 return false; 1115 } 1116 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) { 1117 return false; 1118 } 1119 1120 int result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config); 1121 return result == TvInputHal.SUCCESS; 1122 } 1123 } 1124 stopCapture(TvStreamConfig config)1125 private boolean stopCapture(TvStreamConfig config) { 1126 synchronized (mImplLock) { 1127 if (mReleased) { 1128 return false; 1129 } 1130 if (config == null) { 1131 return false; 1132 } 1133 1134 int result = mHal.removeStream(mInfo.getDeviceId(), config); 1135 return result == TvInputHal.SUCCESS; 1136 } 1137 } 1138 updateAudioSourceLocked()1139 private boolean updateAudioSourceLocked() { 1140 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) { 1141 return false; 1142 } 1143 AudioDevicePort previousSource = mAudioSource; 1144 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress()); 1145 return mAudioSource == null ? (previousSource != null) 1146 : !mAudioSource.equals(previousSource); 1147 } 1148 updateAudioSinkLocked()1149 private boolean updateAudioSinkLocked() { 1150 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) { 1151 return false; 1152 } 1153 List<AudioDevicePort> previousSink = mAudioSink; 1154 mAudioSink = new ArrayList<>(); 1155 if (mOverrideAudioType == AudioManager.DEVICE_NONE) { 1156 findAudioSinkFromAudioPolicy(mAudioSink); 1157 } else { 1158 AudioDevicePort audioSink = 1159 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress); 1160 if (audioSink != null) { 1161 mAudioSink.add(audioSink); 1162 } 1163 } 1164 1165 // Returns true if mAudioSink and previousSink differs. 1166 if (mAudioSink.size() != previousSink.size()) { 1167 return true; 1168 } 1169 previousSink.removeAll(mAudioSink); 1170 return !previousSink.isEmpty(); 1171 } 1172 handleAudioSinkUpdated()1173 private void handleAudioSinkUpdated() { 1174 synchronized (mImplLock) { 1175 updateAudioConfigLocked(); 1176 } 1177 } 1178 1179 @Override overrideAudioSink(int audioType, String audioAddress, int samplingRate, int channelMask, int format)1180 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate, 1181 int channelMask, int format) { 1182 synchronized (mImplLock) { 1183 mOverrideAudioType = audioType; 1184 mOverrideAudioAddress = audioAddress; 1185 1186 mDesiredSamplingRate = samplingRate; 1187 mDesiredChannelMask = channelMask; 1188 mDesiredFormat = format; 1189 1190 updateAudioConfigLocked(); 1191 } 1192 } 1193 onMediaStreamVolumeChanged()1194 public void onMediaStreamVolumeChanged() { 1195 synchronized (mImplLock) { 1196 updateAudioConfigLocked(); 1197 } 1198 } 1199 } 1200 1201 interface Listener { onStateChanged(String inputId, int state)1202 void onStateChanged(String inputId, int state); onHardwareDeviceAdded(TvInputHardwareInfo info)1203 void onHardwareDeviceAdded(TvInputHardwareInfo info); onHardwareDeviceRemoved(TvInputHardwareInfo info)1204 void onHardwareDeviceRemoved(TvInputHardwareInfo info); onHdmiDeviceAdded(HdmiDeviceInfo device)1205 void onHdmiDeviceAdded(HdmiDeviceInfo device); onHdmiDeviceRemoved(HdmiDeviceInfo device)1206 void onHdmiDeviceRemoved(HdmiDeviceInfo device); onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device)1207 void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device); 1208 } 1209 1210 private class ListenerHandler extends Handler { 1211 private static final int STATE_CHANGED = 1; 1212 private static final int HARDWARE_DEVICE_ADDED = 2; 1213 private static final int HARDWARE_DEVICE_REMOVED = 3; 1214 private static final int HDMI_DEVICE_ADDED = 4; 1215 private static final int HDMI_DEVICE_REMOVED = 5; 1216 private static final int HDMI_DEVICE_UPDATED = 6; 1217 private static final int TVINPUT_INFO_ADDED = 7; 1218 1219 @Override handleMessage(Message msg)1220 public final void handleMessage(Message msg) { 1221 switch (msg.what) { 1222 case STATE_CHANGED: { 1223 String inputId = (String) msg.obj; 1224 int state = msg.arg1; 1225 mListener.onStateChanged(inputId, state); 1226 break; 1227 } 1228 case HARDWARE_DEVICE_ADDED: { 1229 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj; 1230 mListener.onHardwareDeviceAdded(info); 1231 break; 1232 } 1233 case HARDWARE_DEVICE_REMOVED: { 1234 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj; 1235 mListener.onHardwareDeviceRemoved(info); 1236 break; 1237 } 1238 case HDMI_DEVICE_ADDED: { 1239 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj; 1240 mListener.onHdmiDeviceAdded(info); 1241 break; 1242 } 1243 case HDMI_DEVICE_REMOVED: { 1244 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj; 1245 mListener.onHdmiDeviceRemoved(info); 1246 break; 1247 } 1248 case HDMI_DEVICE_UPDATED: { 1249 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj; 1250 String inputId; 1251 synchronized (mLock) { 1252 inputId = mHdmiInputIdMap.get(info.getId()); 1253 } 1254 if (inputId != null) { 1255 mListener.onHdmiDeviceUpdated(inputId, info); 1256 } else { 1257 Slog.w(TAG, "Could not resolve input ID matching the device info; " 1258 + "ignoring."); 1259 } 1260 break; 1261 } 1262 case TVINPUT_INFO_ADDED: { 1263 int deviceId = msg.arg1; 1264 int cableConnectionStatus = msg.arg2; 1265 Connection connection =(Connection)msg.obj; 1266 1267 int previousConfigsLength = connection.getConfigsLengthLocked(); 1268 int previousCableConnectionStatus = connection.getInputStateLocked(); 1269 String inputId = mHardwareInputIdMap.get(deviceId); 1270 1271 if (inputId != null) { 1272 if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) { 1273 if (previousCableConnectionStatus != connection.getInputStateLocked()) { 1274 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, 1275 connection.getInputStateLocked(), 0, inputId).sendToTarget(); 1276 } 1277 } else { 1278 if ((previousConfigsLength == 0) 1279 != (connection.getConfigsLengthLocked() == 0)) { 1280 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, 1281 connection.getInputStateLocked(), 0, inputId).sendToTarget(); 1282 } 1283 } 1284 } 1285 break; 1286 } 1287 default: { 1288 Slog.w(TAG, "Unhandled message: " + msg); 1289 break; 1290 } 1291 } 1292 } 1293 } 1294 1295 // Listener implementations for HdmiControlService 1296 1297 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub { 1298 @Override onReceived(HdmiHotplugEvent event)1299 public void onReceived(HdmiHotplugEvent event) { 1300 synchronized (mLock) { 1301 mHdmiStateMap.put(event.getPort(), event.isConnected()); 1302 TvInputHardwareInfo hardwareInfo = 1303 findHardwareInfoForHdmiPortLocked(event.getPort()); 1304 if (hardwareInfo == null) { 1305 return; 1306 } 1307 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId()); 1308 if (inputId == null) { 1309 return; 1310 } 1311 // No HDMI hotplug does not necessarily mean disconnected, as old devices may 1312 // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to 1313 // denote unknown state. 1314 int state = event.isConnected() 1315 ? INPUT_STATE_CONNECTED 1316 : INPUT_STATE_CONNECTED_STANDBY; 1317 mHandler.obtainMessage( 1318 ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget(); 1319 } 1320 } 1321 } 1322 1323 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub { 1324 @Override onStatusChanged(HdmiDeviceInfo deviceInfo, int status)1325 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) { 1326 if (!deviceInfo.isSourceType()) return; 1327 synchronized (mLock) { 1328 int messageType = 0; 1329 Object obj = null; 1330 switch (status) { 1331 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: { 1332 if (findHdmiDeviceInfo(deviceInfo.getId()) == null) { 1333 mHdmiDeviceList.add(deviceInfo); 1334 } else { 1335 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring."); 1336 return; 1337 } 1338 messageType = ListenerHandler.HDMI_DEVICE_ADDED; 1339 obj = deviceInfo; 1340 break; 1341 } 1342 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: { 1343 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId()); 1344 if (!mHdmiDeviceList.remove(originalDeviceInfo)) { 1345 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring."); 1346 return; 1347 } 1348 messageType = ListenerHandler.HDMI_DEVICE_REMOVED; 1349 obj = deviceInfo; 1350 break; 1351 } 1352 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: { 1353 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId()); 1354 if (!mHdmiDeviceList.remove(originalDeviceInfo)) { 1355 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring."); 1356 return; 1357 } 1358 mHdmiDeviceList.add(deviceInfo); 1359 messageType = ListenerHandler.HDMI_DEVICE_UPDATED; 1360 obj = deviceInfo; 1361 break; 1362 } 1363 } 1364 1365 Message msg = mHandler.obtainMessage(messageType, 0, 0, obj); 1366 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) { 1367 msg.sendToTarget(); 1368 } else { 1369 mPendingHdmiDeviceEvents.add(msg); 1370 } 1371 } 1372 } 1373 findHdmiDeviceInfo(int id)1374 private HdmiDeviceInfo findHdmiDeviceInfo(int id) { 1375 for (HdmiDeviceInfo info : mHdmiDeviceList) { 1376 if (info.getId() == id) { 1377 return info; 1378 } 1379 } 1380 return null; 1381 } 1382 } 1383 1384 private final class HdmiSystemAudioModeChangeListener extends 1385 IHdmiSystemAudioModeChangeListener.Stub { 1386 @Override onStatusChanged(boolean enabled)1387 public void onStatusChanged(boolean enabled) throws RemoteException { 1388 synchronized (mLock) { 1389 for (int i = 0; i < mConnections.size(); ++i) { 1390 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked(); 1391 if (impl != null) { 1392 impl.handleAudioSinkUpdated(); 1393 } 1394 } 1395 } 1396 } 1397 } 1398 } 1399