191120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim/* 291120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * Copyright (C) 2014 The Android Open Source Project 391120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * 491120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * Licensed under the Apache License, Version 2.0 (the "License"); 591120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * you may not use this file except in compliance with the License. 691120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * You may obtain a copy of the License at 791120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * 891120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * http://www.apache.org/licenses/LICENSE-2.0 991120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * 1091120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * Unless required by applicable law or agreed to in writing, software 1191120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * distributed under the License is distributed on an "AS IS" BASIS, 1291120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1391120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * See the License for the specific language governing permissions and 1491120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * limitations under the License. 1591120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim */ 1691120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kimpackage android.hardware.hdmi; 1791120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim 18e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jangimport android.annotation.NonNull; 1966d1eb285b129836d1b3c392ed609283c0dbf830Jinsuk Kimimport android.annotation.SystemApi; 2012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.hardware.hdmi.HdmiRecordSources.RecordSource; 2112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.hardware.hdmi.HdmiTimerRecordSources.TimerRecordSource; 22a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kimimport android.os.RemoteException; 23a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kimimport android.util.Log; 2466d1eb285b129836d1b3c392ed609283c0dbf830Jinsuk Kim 252b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heoimport libcore.util.EmptyArray; 262b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo 27bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kimimport java.util.Collections; 28bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kimimport java.util.List; 29bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim 3091120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim/** 3191120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system 3291120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * which acts as TV/Display. It provides with methods that manage, interact with other 3391120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim * devices on the CEC bus. 3466d1eb285b129836d1b3c392ed609283c0dbf830Jinsuk Kim * 3566d1eb285b129836d1b3c392ed609283c0dbf830Jinsuk Kim * @hide 3691120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim */ 3766d1eb285b129836d1b3c392ed609283c0dbf830Jinsuk Kim@SystemApi 38119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kimpublic final class HdmiTvClient extends HdmiClient { 3991120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim private static final String TAG = "HdmiTvClient"; 4091120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim 41f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang /** 42b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim * Size of MHL register for vendor command 43f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang */ 44b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim public static final int VENDOR_DATA_SIZE = 16; 45f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang 462b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo /* package */ HdmiTvClient(IHdmiControlService service) { 47119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim super(service); 4891120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim } 49a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 50a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // Factory method for HdmiTvClient. 51a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim // Declared package-private. Accessed by HdmiControlManager only. 522b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo /* package */ static HdmiTvClient create(IHdmiControlService service) { 53a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim return new HdmiTvClient(service); 54a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 55a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 56a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang @Override 57119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim public int getDeviceType() { 5861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang return HdmiDeviceInfo.DEVICE_TV; 59119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim } 60119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim 61a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim /** 62a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * Callback interface used to get the result of {@link #deviceSelect}. 63a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim */ 64a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim public interface SelectCallback { 65a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim /** 66a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * Called when the operation is finished. 67a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * 68a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * @param result the result value of {@link #deviceSelect} 69a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim */ 70a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim void onComplete(int result); 71a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 72a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 73a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim /** 742b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * Selects a CEC logical device to be a new active source. 75a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim * 766ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim * @param logicalAddress logical address of the device to select 776ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim * @param callback callback to get the result with 786ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim * @throws {@link IllegalArgumentException} if the {@code callback} is null 79a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim */ 80e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) { 81e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang if (callback == null) { 82e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang throw new IllegalArgumentException("callback must not be null."); 83e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang } 84a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim try { 85a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim mService.deviceSelect(logicalAddress, getCallbackWrapper(callback)); 86a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } catch (RemoteException e) { 87a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim Log.e(TAG, "failed to select device: ", e); 88a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 89a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 90a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim 91f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) { 92f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang return new IHdmiControlCallback.Stub() { 93f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang @Override 94f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang public void onComplete(int result) { 95f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang callback.onComplete(result); 96f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 97f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang }; 98f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 99f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang 10041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang /** 1012b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * Selects a HDMI port to be a new route path. 1026ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim * 1036ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim * @param portId HDMI port to select 1046ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim * @param callback callback to get the result with 1056ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim * @throws {@link IllegalArgumentException} if the {@code callback} is null 1066ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim */ 1076ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim public void portSelect(int portId, @NonNull SelectCallback callback) { 1086ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim if (callback == null) { 1096ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim throw new IllegalArgumentException("Callback must not be null"); 1106ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim } 1116ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim try { 1126ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim mService.portSelect(portId, getCallbackWrapper(callback)); 1136ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim } catch (RemoteException e) { 1146ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim Log.e(TAG, "failed to select port: ", e); 1156ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim } 1166ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim } 1176ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim 1186ffb03816f7410c001072a25c3e4161a2da4f501Jinsuk Kim /** 119d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim * Callback interface used to get the input change event. 120d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim */ 121d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim public interface InputChangeListener { 122d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim /** 123d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim * Called when the input was changed. 124d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim * 125d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim * @param info newly selected HDMI input 126d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim */ 127d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim void onChanged(HdmiDeviceInfo info); 128d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim } 129d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim 130d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim /** 1312b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * Sets the listener used to get informed of the input change event. 132d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim * 133d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim * @param listener listener object 134d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim */ 135d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim public void setInputChangeListener(InputChangeListener listener) { 136d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim if (listener == null) { 137d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim throw new IllegalArgumentException("listener must not be null."); 138d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim } 139d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim try { 140d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim mService.setInputChangeListener(getListenerWrapper(listener)); 141d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim } catch (RemoteException e) { 142d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim Log.e("TAG", "Failed to set InputChangeListener:", e); 143d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim } 144d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim } 145d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim 146f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang private static IHdmiInputChangeListener getListenerWrapper(final InputChangeListener listener) { 147f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang return new IHdmiInputChangeListener.Stub() { 148f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang @Override 149f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang public void onChanged(HdmiDeviceInfo info) { 150f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang listener.onChanged(info); 151f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 152f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang }; 153f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 154f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang 155d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim /** 156bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim * Returns all the CEC devices connected to TV. 157bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim * 158bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim * @return list of {@link HdmiDeviceInfo} for connected CEC devices. 159bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim * Empty list is returned if there is none. 160bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim */ 161bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim public List<HdmiDeviceInfo> getDeviceList() { 162bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim try { 163bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim return mService.getDeviceList(); 164bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim } catch (RemoteException e) { 165bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim Log.e("TAG", "Failed to call getDeviceList():", e); 166bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim return Collections.<HdmiDeviceInfo>emptyList(); 167bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim } 168bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim } 169bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim 170bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim /** 171c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim * Sets system audio mode. 172c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim * 173c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim * @param enabled set to {@code true} to enable the mode; otherwise {@code false} 174c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim * @param callback callback to get the result with 175c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim * @throws {@link IllegalArgumentException} if the {@code callback} is null 176c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim */ 177c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim public void setSystemAudioMode(boolean enabled, SelectCallback callback) { 178c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim try { 179c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim mService.setSystemAudioMode(enabled, getCallbackWrapper(callback)); 180c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim } catch (RemoteException e) { 181c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim Log.e(TAG, "failed to set system audio mode:", e); 182c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim } 183c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim } 184c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim 185c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim /** 186c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim * Sets system audio volume. 18741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang * 18841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang * @param oldIndex current volume index 18941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang * @param newIndex volume index to be set 19041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang * @param maxIndex maximum volume index 19141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang */ 19241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang public void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex) { 19341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang try { 19441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang mService.setSystemAudioVolume(oldIndex, newIndex, maxIndex); 19541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang } catch (RemoteException e) { 19641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang Log.e(TAG, "failed to set volume: ", e); 19741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang } 19841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang } 19941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang 20041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang /** 201c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim * Sets system audio mute status. 20241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang * 20341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang * @param mute {@code true} if muted; otherwise, {@code false} 20441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang */ 20541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang public void setSystemAudioMute(boolean mute) { 20641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang try { 20741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang mService.setSystemAudioMute(mute); 20841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang } catch (RemoteException e) { 20941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang Log.e(TAG, "failed to set mute: ", e); 21041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang } 21141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang } 21241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang 213a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang /** 214c6563961e5cce5cfa781c5a956b4e51e3b440883Jinsuk Kim * Sets record listener. 21512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * 21612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang * @param listener 217a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang */ 218e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang public void setRecordListener(@NonNull HdmiRecordListener listener) { 219e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang if (listener == null) { 220e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang throw new IllegalArgumentException("listener must not be null."); 221e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang } 222a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang try { 22312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang mService.setHdmiRecordListener(getListenerWrapper(listener)); 224a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang } catch (RemoteException e) { 22512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang Log.e(TAG, "failed to set record listener.", e); 226a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang } 227a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang } 228a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang 2298d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim /** 2308d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim * Sends a <Standby> command to other device. 2318d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim * 2328d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim * @param deviceId device id to send the command to 2338d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim */ 2348d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim public void sendStandby(int deviceId) { 2358d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim try { 2368d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim mService.sendStandby(getDeviceType(), deviceId); 2378d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim } catch (RemoteException e) { 2388d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim Log.e(TAG, "sendStandby threw exception ", e); 2398d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim } 2408d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim } 2418d115eb18fce5b85538239e2373c3efd28e46986Jinsuk Kim 242f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang private static IHdmiRecordListener getListenerWrapper(final HdmiRecordListener callback) { 243f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang return new IHdmiRecordListener.Stub() { 244f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang @Override 245f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang public byte[] getOneTouchRecordSource(int recorderAddress) { 246f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang HdmiRecordSources.RecordSource source = 2472b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo callback.onOneTouchRecordSourceRequested(recorderAddress); 248f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang if (source == null) { 249f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang return EmptyArray.BYTE; 250f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 251f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang byte[] data = new byte[source.getDataSize(true)]; 252f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang source.toByteArray(true, data, 0); 253f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang return data; 254f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 255f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang 256f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang @Override 257326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang public void onOneTouchRecordResult(int recorderAddress, int result) { 258326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang callback.onOneTouchRecordResult(recorderAddress, result); 259f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 260f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang 261f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang @Override 262326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang public void onTimerRecordingResult(int recorderAddress, int result) { 263326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang callback.onTimerRecordingResult(recorderAddress, 264f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang HdmiRecordListener.TimerStatusData.parseFrom(result)); 265f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 266f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang 267f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang @Override 268326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang public void onClearTimerRecordingResult(int recorderAddress, int result) { 269326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang callback.onClearTimerRecordingResult(recorderAddress, result); 270f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 271f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang }; 272f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 273f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang 274a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang /** 2752b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * Starts one touch recording with the given recorder address and recorder source. 276bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * <p> 277bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * Usage 278a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang * <pre> 279a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang * HdmiTvClient tvClient = ....; 280a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang * // for own source. 2812b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * OwnSource ownSource = HdmiRecordSources.ofOwnSource(); 282bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * tvClient.startOneTouchRecord(recorderAddress, ownSource); 283a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang * </pre> 284a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang */ 285e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang public void startOneTouchRecord(int recorderAddress, @NonNull RecordSource source) { 286e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang if (source == null) { 287e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang throw new IllegalArgumentException("source must not be null."); 288e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang } 289e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang 290a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang try { 291a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang byte[] data = new byte[source.getDataSize(true)]; 292a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang source.toByteArray(true, data, 0); 293bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang mService.startOneTouchRecord(recorderAddress, data); 294bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang } catch (RemoteException e) { 295bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang Log.e(TAG, "failed to start record: ", e); 296bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang } 297bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang } 298bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang 299bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang /** 3002b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * Stops one touch record. 301b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * 302b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * @param recorderAddress recorder address where recoding will be stopped 303b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang */ 304b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang public void stopOneTouchRecord(int recorderAddress) { 305b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang try { 306b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang mService.stopOneTouchRecord(recorderAddress); 307b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang } catch (RemoteException e) { 308b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang Log.e(TAG, "failed to stop record: ", e); 309b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang } 310b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang } 311b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang 312b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang /** 3132b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * Starts timer recording with the given recoder address and recorder source. 314bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * <p> 315bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * Usage 316bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * <pre> 317bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * HdmiTvClient tvClient = ....; 318bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * // create timer info 319bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * TimerInfo timerInfo = HdmiTimerRecourdSources.timerInfoOf(...); 320bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * // for digital source. 321bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * DigitalServiceSource recordSource = HdmiRecordSources.ofDigitalService(...); 322bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * // create timer recording source. 323bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * TimerRecordSource source = HdmiTimerRecourdSources.ofDigitalSource(timerInfo, recordSource); 324bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * tvClient.startTimerRecording(recorderAddress, source); 325bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang * </pre> 326b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * 327b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * @param recorderAddress target recorder address 328b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * @param sourceType type of record source. It should be one of 329b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * {@link HdmiControlManager#TIMER_RECORDING_TYPE_DIGITAL}, 330b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * {@link HdmiControlManager#TIMER_RECORDING_TYPE_ANALOGUE}, 331b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * {@link HdmiControlManager#TIMER_RECORDING_TYPE_EXTERNAL}. 332b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * @param source record source to be used 333b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang */ 334b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang public void startTimerRecording(int recorderAddress, int sourceType, TimerRecordSource source) { 335e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang if (source == null) { 336e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang throw new IllegalArgumentException("source must not be null."); 337e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang } 338e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang 339b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang checkTimerRecordingSourceType(sourceType); 340b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang 341b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang try { 342b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang byte[] data = new byte[source.getDataSize()]; 343b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang source.toByteArray(data, 0); 344b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang mService.startTimerRecording(recorderAddress, sourceType, data); 345b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang } catch (RemoteException e) { 346b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang Log.e(TAG, "failed to start record: ", e); 347b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang } 348b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang } 349b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang 350b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang private void checkTimerRecordingSourceType(int sourceType) { 351b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang switch (sourceType) { 352b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang case HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL: 353b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang case HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE: 354b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang case HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL: 355b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang break; 356b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang default: 357b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang throw new IllegalArgumentException("Invalid source type:" + sourceType); 358b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang } 359b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang } 360b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang 361b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang /** 3622b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * Clears timer recording with the given recorder address and recording source. 363b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang * For more details, please refer {@link #startTimerRecording(int, int, TimerRecordSource)}. 364bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang */ 365b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang public void clearTimerRecording(int recorderAddress, int sourceType, TimerRecordSource source) { 366e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang if (source == null) { 367e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang throw new IllegalArgumentException("source must not be null."); 368e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang } 369e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang 370b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang checkTimerRecordingSourceType(sourceType); 371bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang try { 372bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang byte[] data = new byte[source.getDataSize()]; 373bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang source.toByteArray(data, 0); 374b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang mService.clearTimerRecording(recorderAddress, sourceType, data); 375a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang } catch (RemoteException e) { 376a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang Log.e(TAG, "failed to start record: ", e); 377a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang } 378a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang } 379a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang 380f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang /** 381b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim * Interface used to get incoming MHL vendor command. 382f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang */ 383b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim public interface HdmiMhlVendorCommandListener { 384f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang void onReceived(int portId, int offset, int length, byte[] data); 385a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim } 386a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang 387f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang /** 3882b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * Sets {@link HdmiMhlVendorCommandListener} to get incoming MHL vendor command. 389f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang * 390b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim * @param listener to receive incoming MHL vendor command 391f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang */ 392b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim public void setHdmiMhlVendorCommandListener(HdmiMhlVendorCommandListener listener) { 393f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang if (listener == null) { 394f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang throw new IllegalArgumentException("listener must not be null."); 395f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 396f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang try { 397b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim mService.addHdmiMhlVendorCommandListener(getListenerWrapper(listener)); 398f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } catch (RemoteException e) { 399b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim Log.e(TAG, "failed to set hdmi mhl vendor command listener: ", e); 400f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 401d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim } 402d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim 403b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim private IHdmiMhlVendorCommandListener getListenerWrapper( 404b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim final HdmiMhlVendorCommandListener listener) { 405b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim return new IHdmiMhlVendorCommandListener.Stub() { 406a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang @Override 407f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang public void onReceived(int portId, int offset, int length, byte[] data) { 408f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang listener.onReceived(portId, offset, length, data); 40912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang } 410f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang }; 411f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 41212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang 413f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang /** 4142b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo * Sends MHL vendor command to the device connected to a port of the given portId. 415f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang * 416b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim * @param portId id of port to send MHL vendor command 417f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang * @param offset offset in the in given data 418f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang * @param length length of data. offset + length should be bound to length of data. 419b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim * @param data container for vendor command data. It should be 16 bytes. 420f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang * @throws IllegalArgumentException if the given parameters are invalid 421f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang */ 422b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim public void sendMhlVendorCommand(int portId, int offset, int length, byte[] data) { 423b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim if (data == null || data.length != VENDOR_DATA_SIZE) { 424b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim throw new IllegalArgumentException("Invalid vendor command data."); 425f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 426b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim if (offset < 0 || offset >= VENDOR_DATA_SIZE) { 427f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang throw new IllegalArgumentException("Invalid offset:" + offset); 428f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 429b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim if (length < 0 || offset + length > VENDOR_DATA_SIZE) { 430f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang throw new IllegalArgumentException("Invalid length:" + length); 431f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 432e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang 433f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang try { 434b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim mService.sendMhlVendorCommand(portId, offset, length, data); 435f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } catch (RemoteException e) { 436b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim Log.e(TAG, "failed to send vendor command: ", e); 437f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang } 438a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang } 43991120c541ac0c8c5e256b75759c884b4d6d664fcJinsuk Kim} 440