blob: f75731dd4beee6aea8658f21b97e80d89819f684 [file] [log] [blame]
Yao Chenc4d442f2016-04-08 11:33:47 -07001/*
2 * Copyright (C) 2016 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
17package com.android.car;
18
19import android.content.Context;
20import android.media.AudioManager;
21import android.media.IAudioService;
22import android.media.IVolumeController;
23import android.os.Handler;
24import android.os.Looper;
25import android.os.Message;
26import android.os.RemoteCallbackList;
27import android.os.RemoteException;
28import android.os.ServiceManager;
Yao Chenf1628112016-04-29 10:36:08 -070029import android.telecom.TelecomManager;
Yao Chenc4d442f2016-04-08 11:33:47 -070030import android.util.Log;
31import android.util.Pair;
32import android.util.SparseArray;
33import android.view.KeyEvent;
34
35import com.android.car.CarVolumeService.CarVolumeController;
36import com.android.car.hal.AudioHalService;
37import com.android.internal.annotations.GuardedBy;
38
Keun-young Park7ea21072016-04-27 17:39:50 -070039import java.util.HashMap;
40import java.util.Map;
41
Yao Chenc4d442f2016-04-08 11:33:47 -070042/**
43 * A factory class to create {@link com.android.car.CarVolumeService.CarVolumeController} based
44 * on car properties.
45 */
46public class CarVolumeControllerFactory {
Yao Chenf1628112016-04-29 10:36:08 -070047 // STOPSHIP if true.
48 private static final boolean DBG = true;
Yao Chenc4d442f2016-04-08 11:33:47 -070049
50 public static CarVolumeController createCarVolumeController(Context context,
51 CarAudioService audioService, AudioHalService audioHal, CarInputService inputService) {
52 final boolean volumeSupported = audioHal.isAudioVolumeSupported();
53
54 // Case 1: Car Audio Module does not support volume controls
55 if (!volumeSupported) {
56 return new SimpleCarVolumeController(context);
57 }
58 return new CarExternalVolumeController(context, audioService, audioHal, inputService);
59 }
60
Yao Chenf1628112016-04-29 10:36:08 -070061 public static boolean interceptVolKeyBeforeDispatching(Context context) {
62 Log.d(CarLog.TAG_AUDIO, "interceptVolKeyBeforeDispatching");
63
64 TelecomManager telecomManager = (TelecomManager)
65 context.getSystemService(Context.TELECOM_SERVICE);
66 if (telecomManager != null && telecomManager.isRinging()) {
67 // If an incoming call is ringing, either VOLUME key means
68 // "silence ringer". This is consistent with current android phone's behavior
69 Log.i(CarLog.TAG_AUDIO, "interceptKeyBeforeQueueing:"
70 + " VOLUME key-down while ringing: Silence ringer!");
71
72 // Silence the ringer. (It's safe to call this
73 // even if the ringer has already been silenced.)
74 telecomManager.silenceRinger();
75 return true;
76 }
77 return false;
78 }
79
80 public static boolean isVolumeKey(KeyEvent event) {
81 return event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN
82 || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP;
83 }
84
Yao Chenc4d442f2016-04-08 11:33:47 -070085 /**
86 * To control volumes through {@link android.media.AudioManager} when car audio module does not
87 * support volume controls.
88 */
89 public static final class SimpleCarVolumeController extends CarVolumeController {
90 private final AudioManager mAudioManager;
91 private final Context mContext;
92
93 public SimpleCarVolumeController(Context context) {
94 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
95 mContext = context;
96 }
97
98 @Override
99 void init() {
100 }
101
102 @Override
103 public void setStreamVolume(int stream, int index, int flags) {
Yao Chenf1628112016-04-29 10:36:08 -0700104 if (DBG) {
105 Log.d(CarLog.TAG_AUDIO, "setStreamVolume " + stream + " " + index + " " + flags);
106 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700107 mAudioManager.setStreamVolume(stream, index, flags);
108 }
109
110 @Override
111 public int getStreamVolume(int stream) {
112 return mAudioManager.getStreamVolume(stream);
113 }
114
115 @Override
116 public void setVolumeController(IVolumeController controller) {
117 mAudioManager.setVolumeController(controller);
118 }
119
120 @Override
121 public int getStreamMaxVolume(int stream) {
122 return mAudioManager.getStreamMaxVolume(stream);
123 }
124
125 @Override
126 public int getStreamMinVolume(int stream) {
127 return mAudioManager.getStreamMinVolume(stream);
128 }
129
130 @Override
131 public boolean onKeyEvent(KeyEvent event) {
Yao Chenf1628112016-04-29 10:36:08 -0700132 if (!isVolumeKey(event)) {
133 return false;
134 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700135 handleVolumeKeyDefault(event);
136 return true;
137 }
138
139 private void handleVolumeKeyDefault(KeyEvent event) {
Yao Chenf1628112016-04-29 10:36:08 -0700140 if (event.getAction() != KeyEvent.ACTION_DOWN
141 || interceptVolKeyBeforeDispatching(mContext)) {
Yao Chenc4d442f2016-04-08 11:33:47 -0700142 return;
143 }
144
145 boolean volUp = event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP;
146 int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
147 | AudioManager.FLAG_FROM_KEY;
148 IAudioService audioService = getAudioService();
149 String pkgName = mContext.getOpPackageName();
150 try {
151 if (audioService != null) {
152 audioService.adjustSuggestedStreamVolume(
153 volUp ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER,
154 AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, CarLog.TAG_INPUT);
155 }
156 } catch (RemoteException e) {
157 Log.e(CarLog.TAG_INPUT, "Error calling android audio service.", e);
158 }
159 }
160
161 private static IAudioService getAudioService() {
162 IAudioService audioService = IAudioService.Stub.asInterface(
163 ServiceManager.checkService(Context.AUDIO_SERVICE));
164 if (audioService == null) {
165 Log.w(CarLog.TAG_INPUT, "Unable to find IAudioService interface.");
166 }
167 return audioService;
168 }
169 }
170
171 /**
172 * The car volume controller to use when the car audio modules supports volume controls.
173 *
174 * Depending on whether the car support audio context and has persistent memory, we need to
175 * handle per context volume change properly.
176 *
177 * Regardless whether car supports audio context or not, we need to keep per audio context
178 * volume internally. If we only support single channel, then we only send the volume change
179 * event when that stream is in focus; Otherwise, we need to adjust the stream volume either on
180 * software mixer level or send it the car audio module if the car support audio context
181 * and multi channel. TODO: Add support for multi channel.
182 *
183 * Per context volume should be persisted, so the volumes can stay the same across boots.
184 * Depending on the hardware property, this can be persisted on car side (or/and android side).
185 * TODO: we need to define one single source of truth if the car has memory.
186 */
187 public static class CarExternalVolumeController extends CarVolumeController
188 implements CarInputService.KeyEventListener, AudioHalService.AudioHalVolumeListener,
189 CarAudioService.AudioContextChangeListener {
190 private static final String TAG = CarLog.TAG_AUDIO + "ExtVolCtrl";
191 private static final int MSG_UPDATE_VOLUME = 0;
192 private static final int MSG_UPDATE_HAL = 1;
193
194 private final Context mContext;
195 private final AudioRoutingPolicy mPolicy;
196 private final AudioHalService mHal;
197 private final CarInputService mInputService;
198 private final CarAudioService mAudioService;
199
200 private int mSupportedAudioContext;
201
202 private boolean mHasExternalMemory;
Keun-young Parkd36a9952016-05-24 10:03:59 -0700203 private boolean mMasterVolumeOnly;
Yao Chenc4d442f2016-04-08 11:33:47 -0700204
205 @GuardedBy("this")
206 private int mCurrentContext = CarVolumeService.DEFAULT_CAR_AUDIO_CONTEXT;
Yao Chen75279c92016-05-06 14:41:37 -0700207 // current logical volume, the key is car audio context
Yao Chenc4d442f2016-04-08 11:33:47 -0700208 @GuardedBy("this")
Yao Chen75279c92016-05-06 14:41:37 -0700209 private final SparseArray<Integer> mCurrentCarContextVolume =
210 new SparseArray<>(VolumeUtils.CAR_AUDIO_CONTEXT.length);
211 // stream volume limit, the key is car audio context type
Yao Chenc4d442f2016-04-08 11:33:47 -0700212 @GuardedBy("this")
Yao Chen75279c92016-05-06 14:41:37 -0700213 private final SparseArray<Integer> mCarContextVolumeMax =
214 new SparseArray<>(VolumeUtils.CAR_AUDIO_CONTEXT.length);
215 // stream volume limit, the key is car audio context type
Yao Chenc4d442f2016-04-08 11:33:47 -0700216 @GuardedBy("this")
Yao Chen75279c92016-05-06 14:41:37 -0700217 private final SparseArray<Integer> mCarContextVolumeMin =
218 new SparseArray<>(VolumeUtils.CAR_AUDIO_CONTEXT.length);
Yao Chenc4d442f2016-04-08 11:33:47 -0700219 @GuardedBy("this")
220 private final RemoteCallbackList<IVolumeController> mVolumeControllers =
221 new RemoteCallbackList<>();
222
223 private final Handler mHandler = new VolumeHandler();
224
225 /**
Yao Chen75279c92016-05-06 14:41:37 -0700226 * Convert an car context to the car stream.
Yao Chenc4d442f2016-04-08 11:33:47 -0700227 *
228 * @return If car supports audio context, then it returns the car audio context. Otherwise,
229 * it returns the physical stream that maps to this logical stream.
230 */
Yao Chen75279c92016-05-06 14:41:37 -0700231 private int carContextToCarStream(int carContext) {
Yao Chenc4d442f2016-04-08 11:33:47 -0700232 if (mSupportedAudioContext == 0) {
233 int physicalStream = mPolicy.getPhysicalStreamForLogicalStream(
Yao Chen75279c92016-05-06 14:41:37 -0700234 AudioHalService.carContextToCarUsage(carContext));
Yao Chenc4d442f2016-04-08 11:33:47 -0700235 return physicalStream;
236 } else {
Yao Chenc4d442f2016-04-08 11:33:47 -0700237 return carContext;
238 }
239 }
240
241 /**
242 * All updates to external components should be posted to this handler to avoid holding
243 * the internal lock while sending updates.
244 */
245 private final class VolumeHandler extends Handler {
246 @Override
247 public void handleMessage(Message msg) {
248 int stream;
249 int volume;
250 switch (msg.what) {
251 case MSG_UPDATE_VOLUME:
252 stream = msg.arg1;
253 int flag = msg.arg2;
254 final int size = mVolumeControllers.beginBroadcast();
255 try {
256 for (int i = 0; i < size; i++) {
257 try {
258 mVolumeControllers.getBroadcastItem(i)
259 .volumeChanged(stream, flag);
260 } catch (RemoteException ignored) {
261 }
262 }
263 } finally {
264 mVolumeControllers.finishBroadcast();
265 }
266 break;
267 case MSG_UPDATE_HAL:
268 stream = msg.arg1;
269 volume = msg.arg2;
Keun-young Parkd36a9952016-05-24 10:03:59 -0700270 synchronized (CarExternalVolumeController.this) {
271 if (mMasterVolumeOnly) {
272 stream = 0;
273 }
274 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700275 mHal.setStreamVolume(stream, volume);
276 break;
277 default:
278 break;
279 }
280 }
281 }
282
283 public CarExternalVolumeController(Context context, CarAudioService audioService,
284 AudioHalService hal, CarInputService inputService) {
285 mContext = context;
286 mAudioService = audioService;
287 mPolicy = audioService.getAudioRoutingPolicy();
288 mHal = hal;
289 mInputService = inputService;
290 }
291
292 @Override
293 void init() {
294 mSupportedAudioContext = mHal.getSupportedAudioVolumeContexts();
295 mHasExternalMemory = mHal.isExternalAudioVolumePersistent();
Keun-young Parkd36a9952016-05-24 10:03:59 -0700296 mMasterVolumeOnly = mHal.isAudioVolumeMasterOnly();
Yao Chenc4d442f2016-04-08 11:33:47 -0700297 synchronized (this) {
298 initVolumeLimitLocked();
299 initCurrentVolumeLocked();
300 }
301 mInputService.setVolumeKeyListener(this);
302 mHal.setVolumeListener(this);
303 mAudioService.setAudioContextChangeListener(Looper.getMainLooper(), this);
304 }
305
306 private void initVolumeLimitLocked() {
Yao Chen75279c92016-05-06 14:41:37 -0700307 for (int i : VolumeUtils.CAR_AUDIO_CONTEXT) {
308 int carStream = carContextToCarStream(i);
Yao Chenc4d442f2016-04-08 11:33:47 -0700309 Pair<Integer, Integer> volumeMinMax = mHal.getStreamVolumeLimit(carStream);
310 int max;
311 int min;
312 if (volumeMinMax == null) {
313 max = 0;
314 min = 0;
315 } else {
316 max = volumeMinMax.second >= 0 ? volumeMinMax.second : 0;
317 min = volumeMinMax.first >=0 ? volumeMinMax.first : 0;
318 }
319 // get default stream volume limit first.
Yao Chen75279c92016-05-06 14:41:37 -0700320 mCarContextVolumeMax.put(i, max);
321 mCarContextVolumeMin.put(i, min);
Yao Chenc4d442f2016-04-08 11:33:47 -0700322 }
323 }
324
325 private void initCurrentVolumeLocked() {
326 if (mHasExternalMemory) {
327 // TODO: read per context volume from audio hal
328 } else {
329 // TODO: read the Android side volume from Settings and pass it to the audio module
330 // Here we just set it to the physical stream volume temporarily.
Keun-young Park021310d2016-04-25 21:09:39 -0700331 // when vhal does not work, get call can take long. For that case,
332 // for the same physical streams, cache initial get results
Keun-young Park7ea21072016-04-27 17:39:50 -0700333 Map<Integer, Integer> volumesPerCarStream = new HashMap<>();
Yao Chen75279c92016-05-06 14:41:37 -0700334 for (int i : VolumeUtils.CAR_AUDIO_CONTEXT) {
335 int carStream = carContextToCarStream(i);
Keun-young Park021310d2016-04-25 21:09:39 -0700336 Integer volume = volumesPerCarStream.get(carStream);
337 if (volume == null) {
Keun-young Parkd36a9952016-05-24 10:03:59 -0700338 volume = Integer.valueOf(mHal.getStreamVolume(mMasterVolumeOnly ? 0 :
339 carStream));
Keun-young Park021310d2016-04-25 21:09:39 -0700340 volumesPerCarStream.put(carStream, volume);
341 }
Yao Chen75279c92016-05-06 14:41:37 -0700342 mCurrentCarContextVolume.put(i, volume);
343 if (DBG) {
344 Log.d(TAG, " init volume, car audio context: " + i + " volume: " + volume);
345 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700346 }
347 }
348 }
349
350 @Override
351 public void setStreamVolume(int stream, int index, int flags) {
352 synchronized (this) {
Yao Chen75279c92016-05-06 14:41:37 -0700353 int carContext;
354 // Currently car context and android logical stream are not
355 // one-to-one mapping. In this API, Android side asks us to change a logical stream
356 // volume. If the current car audio context maps to this logical stream, then we
357 // change the volume for the current car audio context. Otherwise, we change the
358 // volume for the primary mapped car audio context.
359 if (VolumeUtils.carContextToAndroidStream(mCurrentContext) == stream) {
360 carContext = mCurrentContext;
361 } else {
362 carContext = VolumeUtils.androidStreamToCarContext(stream);
363 }
364 if (DBG) {
365 Log.d(TAG, "Receive setStreamVolume logical stream: " + stream + " index: "
366 + index + " flags: " + flags + " maps to car context: " + carContext);
367 }
368 setStreamVolumeInternalLocked(carContext, index, flags);
Yao Chenc4d442f2016-04-08 11:33:47 -0700369 }
370 }
371
Yao Chen75279c92016-05-06 14:41:37 -0700372 private void setStreamVolumeInternalLocked(int carContext, int index, int flags) {
373 if (mCarContextVolumeMax.get(carContext) == null) {
374 Log.e(TAG, "Stream type not supported " + carContext);
Yao Chenc4d442f2016-04-08 11:33:47 -0700375 return;
376 }
Yao Chen75279c92016-05-06 14:41:37 -0700377 int limit = mCarContextVolumeMax.get(carContext);
Yao Chenc4d442f2016-04-08 11:33:47 -0700378 if (index > limit) {
Yao Chen75279c92016-05-06 14:41:37 -0700379 Log.w(TAG, "Volume exceeds volume limit. context: " + carContext
380 + " index: " + index + " limit: " + limit);
Yao Chenc4d442f2016-04-08 11:33:47 -0700381 index = limit;
382 }
383
384 if (index < 0) {
385 index = 0;
386 }
387
Yao Chen75279c92016-05-06 14:41:37 -0700388 if (mCurrentCarContextVolume.get(carContext) == index) {
Yao Chenc4d442f2016-04-08 11:33:47 -0700389 return;
390 }
391
Yao Chen75279c92016-05-06 14:41:37 -0700392 int carStream = carContextToCarStream(carContext);
393 if (DBG) {
394 Log.d(TAG, "Change car stream volume, stream: " + carStream + " volume:" + index);
395 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700396 // For single channel, only adjust the volume when the audio context is the current one.
397 if (mCurrentContext == carContext) {
Yao Chen75279c92016-05-06 14:41:37 -0700398 if (DBG) {
399 Log.d(TAG, "Sending volume change to HAL");
400 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700401 mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_HAL, carStream, index));
402 }
403 // Record the current volume internally.
Yao Chen75279c92016-05-06 14:41:37 -0700404 mCurrentCarContextVolume.put(carContext, index);
405 mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_VOLUME,
406 VolumeUtils.carContextToAndroidStream(carContext),
Yao Chenc4d442f2016-04-08 11:33:47 -0700407 getVolumeUpdateFlag()));
408 }
409
410 @Override
411 public int getStreamVolume(int stream) {
412 synchronized (this) {
Yao Chen75279c92016-05-06 14:41:37 -0700413 if (VolumeUtils.carContextToAndroidStream(mCurrentContext) == stream) {
414 return mCurrentCarContextVolume.get(mCurrentContext);
Yao Chenc4d442f2016-04-08 11:33:47 -0700415 }
Yao Chen75279c92016-05-06 14:41:37 -0700416 return mCurrentCarContextVolume.get(VolumeUtils.androidStreamToCarContext(stream));
Yao Chenc4d442f2016-04-08 11:33:47 -0700417 }
418 }
419
420 @Override
421 public void setVolumeController(IVolumeController controller) {
422 synchronized (this) {
423 mVolumeControllers.register(controller);
424 }
425 }
426
427 @Override
428 public void onVolumeChange(int carStream, int volume, int volumeState) {
429 int flag = getVolumeUpdateFlag();
430 synchronized (this) {
Yao Chen75279c92016-05-06 14:41:37 -0700431 if (DBG) {
432 Log.d(TAG, "onVolumeChange carStream:" + carStream + " volume: " + volume
Keun-young Parkd36a9952016-05-24 10:03:59 -0700433 + " volumeState: " + volumeState);
Yao Chen75279c92016-05-06 14:41:37 -0700434 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700435 // Assume single channel here.
436 int currentLogicalStream = VolumeUtils.carContextToAndroidStream(mCurrentContext);
Yao Chen75279c92016-05-06 14:41:37 -0700437 int currentCarStream = carContextToCarStream(mCurrentContext);
Keun-young Parkd36a9952016-05-24 10:03:59 -0700438 if (mMasterVolumeOnly) { //for master volume only H/W, always assume current stream
439 carStream = currentCarStream;
440 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700441 if (currentCarStream == carStream) {
Yao Chen75279c92016-05-06 14:41:37 -0700442 mCurrentCarContextVolume.put(mCurrentContext, volume);
Yao Chenc4d442f2016-04-08 11:33:47 -0700443 mHandler.sendMessage(
444 mHandler.obtainMessage(MSG_UPDATE_VOLUME, currentLogicalStream, flag));
445 } else {
446 // Hal is telling us a car stream volume has changed, but it is not the current
447 // stream.
448 Log.w(TAG, "Car stream" + carStream
449 + " volume changed, but it is not current stream, ignored.");
450 }
451 }
452 }
453
454 private int getVolumeUpdateFlag() {
455 // TODO: Apply appropriate flags.
456 return AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND;
457 }
458
459 private void updateHalVolumeLocked(final int carStream, final int index) {
460 mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_HAL, carStream, index));
461 }
462
463 @Override
464 public void onVolumeLimitChange(int streamNumber, int volume) {
465 // TODO: How should this update be sent to SystemUI? maybe send a volume update without
466 // showing UI.
467 synchronized (this) {
468 initVolumeLimitLocked();
469 }
470 }
471
472 @Override
473 public int getStreamMaxVolume(int stream) {
474 synchronized (this) {
Yao Chen75279c92016-05-06 14:41:37 -0700475 if (VolumeUtils.carContextToAndroidStream(mCurrentContext) == stream) {
476 return mCarContextVolumeMax.get(mCurrentContext);
477 } else {
478 return mCarContextVolumeMax.get(VolumeUtils.androidStreamToCarContext(stream));
Yao Chenc4d442f2016-04-08 11:33:47 -0700479 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700480 }
481 }
482
483 @Override
484 public int getStreamMinVolume(int stream) {
485 synchronized (this) {
Yao Chen75279c92016-05-06 14:41:37 -0700486 if (VolumeUtils.carContextToAndroidStream(mCurrentContext) == stream) {
487 return mCarContextVolumeMin.get(mCurrentContext);
488 } else {
489 return mCarContextVolumeMin.get(VolumeUtils.androidStreamToCarContext(stream));
Yao Chenc4d442f2016-04-08 11:33:47 -0700490 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700491 }
492 }
493
494 @Override
495 public boolean onKeyEvent(KeyEvent event) {
Yao Chenf1628112016-04-29 10:36:08 -0700496 if (!isVolumeKey(event)) {
497 return false;
498 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700499 final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
Yao Chen75279c92016-05-06 14:41:37 -0700500 if (DBG) {
501 Log.d(TAG, "Receive volume keyevent " + event.toString());
502 }
Yao Chenc4d442f2016-04-08 11:33:47 -0700503 // TODO: properly handle long press on volume key
Yao Chenf1628112016-04-29 10:36:08 -0700504 if (!down || interceptVolKeyBeforeDispatching(mContext)) {
Yao Chenc4d442f2016-04-08 11:33:47 -0700505 return true;
506 }
507
508 synchronized (this) {
Yao Chen75279c92016-05-06 14:41:37 -0700509 int currentVolume = mCurrentCarContextVolume.get(mCurrentContext);
Yao Chenc4d442f2016-04-08 11:33:47 -0700510 switch (event.getKeyCode()) {
511 case KeyEvent.KEYCODE_VOLUME_UP:
Yao Chen75279c92016-05-06 14:41:37 -0700512 setStreamVolumeInternalLocked(mCurrentContext, currentVolume + 1,
Yao Chenc4d442f2016-04-08 11:33:47 -0700513 getVolumeUpdateFlag());
514 break;
515 case KeyEvent.KEYCODE_VOLUME_DOWN:
Yao Chen75279c92016-05-06 14:41:37 -0700516 setStreamVolumeInternalLocked(mCurrentContext, currentVolume - 1,
Yao Chenc4d442f2016-04-08 11:33:47 -0700517 getVolumeUpdateFlag());
518 break;
519 }
520 }
521 return true;
522 }
523
524 @Override
525 public void onContextChange(int primaryFocusContext, int primaryFocusPhysicalStream) {
526 synchronized (this) {
Yao Chen75279c92016-05-06 14:41:37 -0700527 if(DBG) {
528 Log.d(TAG, "Audio context changed from " + mCurrentContext + " to: "
529 + primaryFocusContext + " physical: " + primaryFocusPhysicalStream);
530 }
Yao Chen740c2702016-05-11 15:30:25 -0700531 // if primaryFocusContext is 0, it means nothing is playing or holding focus,
532 // we will keep the last focus context and if the user changes the volume
533 // it will go to the last audio context.
534 if (primaryFocusContext == mCurrentContext || primaryFocusContext == 0) {
Yao Chenc4d442f2016-04-08 11:33:47 -0700535 return;
536 }
537 mCurrentContext = primaryFocusContext;
Yao Chen75279c92016-05-06 14:41:37 -0700538 // if car supports audio context and has external memory, then we don't need to do
539 // anything.
540 if(mSupportedAudioContext != 0 && mHasExternalMemory) {
541 if (DBG) {
542 Log.d(TAG, "Car support audio context and has external memory," +
543 " no volume change needed from car service");
Yao Chenc4d442f2016-04-08 11:33:47 -0700544 }
Yao Chen75279c92016-05-06 14:41:37 -0700545 return;
Yao Chenc4d442f2016-04-08 11:33:47 -0700546 }
Yao Chen75279c92016-05-06 14:41:37 -0700547
548 // Otherwise, we need to tell Hal what the correct volume is for the new context.
549 int currentVolume = mCurrentCarContextVolume.get(primaryFocusContext);
550
Keun-young Parkd36a9952016-05-24 10:03:59 -0700551 int carStreamNumber = (mSupportedAudioContext == 0) ? primaryFocusPhysicalStream :
Yao Chen75279c92016-05-06 14:41:37 -0700552 primaryFocusContext;
553 if (DBG) {
554 Log.d(TAG, "Change volume from: "
555 + mCurrentCarContextVolume.get(mCurrentContext)
556 + " to: "+ currentVolume);
557 }
558 updateHalVolumeLocked(carStreamNumber, currentVolume);
Yao Chenc4d442f2016-04-08 11:33:47 -0700559 }
560 }
561 }
562}