1deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon/*
2deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * Copyright 2014, The Android Open Source Project
3deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon *
4deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * Licensed under the Apache License, Version 2.0 (the "License");
5deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * you may not use this file except in compliance with the License.
6deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * You may obtain a copy of the License at
7deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon *
8deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon *     http://www.apache.org/licenses/LICENSE-2.0
9deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon *
10deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * Unless required by applicable law or agreed to in writing, software
11deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * distributed under the License is distributed on an "AS IS" BASIS,
12deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * See the License for the specific language governing permissions and
14deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * limitations under the License.
15deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon */
16deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
177cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnpackage com.android.server.telecom;
18deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
19deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordonimport android.content.Context;
20deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordonimport android.content.Intent;
2107920f67c86e80fc78086e21fd446b2531c2defdRoboErikimport android.media.AudioAttributes;
22deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordonimport android.media.session.MediaSession;
23731369c3983628e700f161138fe2ea3230033a1aIhab Awadimport android.os.Handler;
24731369c3983628e700f161138fe2ea3230033a1aIhab Awadimport android.os.Looper;
25731369c3983628e700f161138fe2ea3230033a1aIhab Awadimport android.os.Message;
26deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordonimport android.view.KeyEvent;
27deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
28deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon/**
29deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon * Static class to handle listening to the headset media buttons.
30deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon */
318de76915ea2772faeb41705aaaeb65f5b3478ac4Ihab Awadpublic class HeadsetMediaButton extends CallsManagerListenerBase {
32deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
33deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon    // Types of media button presses
34deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon    static final int SHORT_PRESS = 1;
35deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon    static final int LONG_PRESS = 2;
36deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
3707920f67c86e80fc78086e21fd446b2531c2defdRoboErik    private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
3807920f67c86e80fc78086e21fd446b2531c2defdRoboErik            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
3907920f67c86e80fc78086e21fd446b2531c2defdRoboErik            .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build();
4007920f67c86e80fc78086e21fd446b2531c2defdRoboErik
41731369c3983628e700f161138fe2ea3230033a1aIhab Awad    private static final int MSG_MEDIA_SESSION_INITIALIZE = 0;
42731369c3983628e700f161138fe2ea3230033a1aIhab Awad    private static final int MSG_MEDIA_SESSION_SET_ACTIVE = 1;
43731369c3983628e700f161138fe2ea3230033a1aIhab Awad
44deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon    private final MediaSession.Callback mSessionCallback = new MediaSession.Callback() {
45deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon        @Override
46a6e9d758f02ec0654f489725e9b9442796d0b03fRoboErik        public boolean onMediaButtonEvent(Intent intent) {
47deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
48deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon            Log.v(this, "SessionCallback.onMediaButton()...  event = %s.", event);
49deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon            if ((event != null) && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
50731369c3983628e700f161138fe2ea3230033a1aIhab Awad                synchronized (mLock) {
51731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    Log.v(this, "SessionCallback: HEADSETHOOK");
52731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    boolean consumed = handleHeadsetHook(event);
53731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    Log.v(this, "==> handleHeadsetHook(): consumed = %b.", consumed);
54731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    return consumed;
55731369c3983628e700f161138fe2ea3230033a1aIhab Awad                }
56deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon            }
57a6e9d758f02ec0654f489725e9b9442796d0b03fRoboErik            return true;
58deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon        }
59deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon    };
60deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
61731369c3983628e700f161138fe2ea3230033a1aIhab Awad    private final Handler mMediaSessionHandler = new Handler(Looper.getMainLooper()) {
62731369c3983628e700f161138fe2ea3230033a1aIhab Awad        @Override
63731369c3983628e700f161138fe2ea3230033a1aIhab Awad        public void handleMessage(Message msg) {
64731369c3983628e700f161138fe2ea3230033a1aIhab Awad            switch (msg.what) {
65731369c3983628e700f161138fe2ea3230033a1aIhab Awad                case MSG_MEDIA_SESSION_INITIALIZE: {
66731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    MediaSession session = new MediaSession(
67731369c3983628e700f161138fe2ea3230033a1aIhab Awad                            mContext,
68731369c3983628e700f161138fe2ea3230033a1aIhab Awad                            HeadsetMediaButton.class.getSimpleName());
69731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    session.setCallback(mSessionCallback);
70731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    session.setFlags(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY
71731369c3983628e700f161138fe2ea3230033a1aIhab Awad                            | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
72731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    session.setPlaybackToLocal(AUDIO_ATTRIBUTES);
73731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    mSession = session;
74731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    break;
75731369c3983628e700f161138fe2ea3230033a1aIhab Awad                }
76731369c3983628e700f161138fe2ea3230033a1aIhab Awad                case MSG_MEDIA_SESSION_SET_ACTIVE: {
77731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    if (mSession != null) {
78731369c3983628e700f161138fe2ea3230033a1aIhab Awad                        boolean activate = msg.arg1 != 0;
79731369c3983628e700f161138fe2ea3230033a1aIhab Awad                        if (activate != mSession.isActive()) {
80731369c3983628e700f161138fe2ea3230033a1aIhab Awad                            mSession.setActive(activate);
81731369c3983628e700f161138fe2ea3230033a1aIhab Awad                        }
82731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    }
83731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    break;
84731369c3983628e700f161138fe2ea3230033a1aIhab Awad                }
85731369c3983628e700f161138fe2ea3230033a1aIhab Awad                default:
86731369c3983628e700f161138fe2ea3230033a1aIhab Awad                    break;
87731369c3983628e700f161138fe2ea3230033a1aIhab Awad            }
88731369c3983628e700f161138fe2ea3230033a1aIhab Awad        }
89731369c3983628e700f161138fe2ea3230033a1aIhab Awad    };
90deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
91731369c3983628e700f161138fe2ea3230033a1aIhab Awad    private final Context mContext;
92731369c3983628e700f161138fe2ea3230033a1aIhab Awad    private final CallsManager mCallsManager;
93731369c3983628e700f161138fe2ea3230033a1aIhab Awad    private final TelecomSystem.SyncRoot mLock;
94731369c3983628e700f161138fe2ea3230033a1aIhab Awad    private MediaSession mSession;
95deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
96731369c3983628e700f161138fe2ea3230033a1aIhab Awad    public HeadsetMediaButton(
97731369c3983628e700f161138fe2ea3230033a1aIhab Awad            Context context,
98731369c3983628e700f161138fe2ea3230033a1aIhab Awad            CallsManager callsManager,
99731369c3983628e700f161138fe2ea3230033a1aIhab Awad            TelecomSystem.SyncRoot lock) {
100731369c3983628e700f161138fe2ea3230033a1aIhab Awad        mContext = context;
101deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon        mCallsManager = callsManager;
102731369c3983628e700f161138fe2ea3230033a1aIhab Awad        mLock = lock;
103deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
104b771c432c5526ed54e5f07949ace2b99008bc341RoboErik        // Create a MediaSession but don't enable it yet. This is a
105deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon        // replacement for MediaButtonReceiver
106731369c3983628e700f161138fe2ea3230033a1aIhab Awad        mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_INITIALIZE).sendToTarget();
107deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon    }
108deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
109deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon    /**
110deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon     * Handles the wired headset button while in-call.
111deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon     *
112deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon     * @return true if we consumed the event.
113deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon     */
114deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon    private boolean handleHeadsetHook(KeyEvent event) {
115deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon        Log.d(this, "handleHeadsetHook()...%s %s", event.getAction(), event.getRepeatCount());
116deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
117deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon        if (event.isLongPress()) {
118deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon            return mCallsManager.onMediaButton(LONG_PRESS);
119deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon        } else if (event.getAction() == KeyEvent.ACTION_UP && event.getRepeatCount() == 0) {
120deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon            return mCallsManager.onMediaButton(SHORT_PRESS);
121deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon        }
122deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon
123deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon        return true;
124deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon    }
1258128998af91646bae7ae858f60e05075776e0a5cSantos Cordon
1268128998af91646bae7ae858f60e05075776e0a5cSantos Cordon    /** ${inheritDoc} */
1278128998af91646bae7ae858f60e05075776e0a5cSantos Cordon    @Override
1288128998af91646bae7ae858f60e05075776e0a5cSantos Cordon    public void onCallAdded(Call call) {
1291a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn        if (call.isExternalCall()) {
1301a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn            return;
1311a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn        }
132731369c3983628e700f161138fe2ea3230033a1aIhab Awad        mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 1, 0).sendToTarget();
1338128998af91646bae7ae858f60e05075776e0a5cSantos Cordon    }
1348128998af91646bae7ae858f60e05075776e0a5cSantos Cordon
1358128998af91646bae7ae858f60e05075776e0a5cSantos Cordon    /** ${inheritDoc} */
1368128998af91646bae7ae858f60e05075776e0a5cSantos Cordon    @Override
1378128998af91646bae7ae858f60e05075776e0a5cSantos Cordon    public void onCallRemoved(Call call) {
138f15dc33f87f88e21ef745952a68af65c86e1bf1eTyler Gunn        if (call.isExternalCall()) {
139f15dc33f87f88e21ef745952a68af65c86e1bf1eTyler Gunn            return;
140f15dc33f87f88e21ef745952a68af65c86e1bf1eTyler Gunn        }
1418128998af91646bae7ae858f60e05075776e0a5cSantos Cordon        if (!mCallsManager.hasAnyCalls()) {
142731369c3983628e700f161138fe2ea3230033a1aIhab Awad            mMediaSessionHandler.obtainMessage(MSG_MEDIA_SESSION_SET_ACTIVE, 0, 0).sendToTarget();
1438128998af91646bae7ae858f60e05075776e0a5cSantos Cordon        }
1448128998af91646bae7ae858f60e05075776e0a5cSantos Cordon    }
1451a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn
1461a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn    /** ${inheritDoc} */
1471a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn    @Override
1481a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn    public void onExternalCallChanged(Call call, boolean isExternalCall) {
1491a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn        if (isExternalCall) {
1501a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn            onCallRemoved(call);
1511a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn        } else {
1521a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn            onCallAdded(call);
1531a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn        }
1541a40c4f4daf2cdfba3f67383c726f93b85a98b00Tyler Gunn    }
155deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon}
156