1b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan/*
2b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * Copyright (C) 2016 The Android Open Source Project
3b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan *
4b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * Licensed under the Apache License, Version 2.0 (the "License");
5b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * you may not use this file except in compliance with the License.
6b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * You may obtain a copy of the License at
7b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan *
8b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan *      http://www.apache.org/licenses/LICENSE-2.0
9b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan *
10b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * Unless required by applicable law or agreed to in writing, software
11b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * distributed under the License is distributed on an "AS IS" BASIS,
12b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * See the License for the specific language governing permissions and
14b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * limitations under the License.
15b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan */
16b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
17b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanpackage com.android.server.tv;
18b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
19b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport android.content.Context;
20b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport android.os.Handler;
21b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport android.os.IBinder;
22b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport android.os.Looper;
23b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport android.os.Message;
24b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport android.util.ArrayMap;
25b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport android.util.Slog;
26b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
27b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport com.android.server.SystemService;
28b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport com.android.server.Watchdog;
29b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
30b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport java.io.IOException;
31b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport java.util.ArrayList;
32b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanimport java.util.Map;
33b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
34b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan/**
35b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * TvRemoteService represents a system service that allows a connected
36b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * remote control (emote) service to inject white-listed input events
37b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * and call other specified methods for functioning as an emote service.
38b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * <p/>
39b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan * This service is intended for use only by white-listed packages.
40b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan */
41b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnanpublic class TvRemoteService extends SystemService implements Watchdog.Monitor {
42b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private static final String TAG = "TvRemoteService";
43b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private static final boolean DEBUG = false;
44b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private static final boolean DEBUG_KEYS = false;
45b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
46b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private Map<IBinder, UinputBridge> mBridgeMap = new ArrayMap();
47b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private Map<IBinder, TvRemoteProviderProxy> mProviderMap = new ArrayMap();
48b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private ArrayList<TvRemoteProviderProxy> mProviderList = new ArrayList<>();
49b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
50b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    /**
51b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan     * State guarded by mLock.
52b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan     *  This is the second lock in sequence for an incoming call.
53b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan     *  The first lock is always {@link TvRemoteProviderProxy#mLock}
54b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan     *
55b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan     *  There are currently no methods that break this sequence.
56b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan     *  Special note:
57b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan     *  Outgoing call informInputBridgeConnected(), which is called from
58b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan     *  openInputBridgeInternalLocked() uses a handler thereby relinquishing held locks.
59b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan     */
60b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private final Object mLock = new Object();
61b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
62b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    public final UserHandler mHandler;
63b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
64b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    public TvRemoteService(Context context) {
65b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        super(context);
66b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        mHandler = new UserHandler(new UserProvider(TvRemoteService.this), context);
67b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        Watchdog.getInstance().addMonitor(this);
68b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
69b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
70b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    @Override
71b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    public void onStart() {
72b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (DEBUG) Slog.d(TAG, "onStart()");
73b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
74b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
75b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    @Override
76b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    public void monitor() {
77b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        synchronized (mLock) { /* check for deadlock */ }
78b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
79b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
80b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    @Override
81b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    public void onBootPhase(int phase) {
82b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
83b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG) Slog.d(TAG, "PHASE_THIRD_PARTY_APPS_CAN_START");
84b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            mHandler.sendEmptyMessage(UserHandler.MSG_START);
85b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
86b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
87b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
88b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    //Outgoing calls.
89b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void informInputBridgeConnected(IBinder token) {
90b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        mHandler.obtainMessage(UserHandler.MSG_INPUT_BRIDGE_CONNECTED, 0, 0, token).sendToTarget();
91b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
92b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
93b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    // Incoming calls.
94b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void openInputBridgeInternalLocked(TvRemoteProviderProxy provider, IBinder token,
95b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                                               String name, int width, int height,
96b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                                               int maxPointers) {
97b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (DEBUG) {
98b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            Slog.d(TAG, "openInputBridgeInternalLocked(), token: " + token + ", name: " + name +
99b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    ", width: " + width + ", height: " + height + ", maxPointers: " + maxPointers);
100b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
101b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
102b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        try {
103b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            //Create a new bridge, if one does not exist already
104b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (mBridgeMap.containsKey(token)) {
105b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (DEBUG) Slog.d(TAG, "RemoteBridge already exists");
106b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                // Respond back with success.
107b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                informInputBridgeConnected(token);
108b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                return;
109b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
110b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
111b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            UinputBridge inputBridge = new UinputBridge(token, name, width, height, maxPointers);
112b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
113b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            mBridgeMap.put(token, inputBridge);
114b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            mProviderMap.put(token, provider);
115b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
116b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            // Respond back with success.
117b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            informInputBridgeConnected(token);
118b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
119b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        } catch (IOException ioe) {
120b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            Slog.e(TAG, "Cannot create device for " + name);
121b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
122b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
123b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
124b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void closeInputBridgeInternalLocked(IBinder token) {
125b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (DEBUG) {
126b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            Slog.d(TAG, "closeInputBridgeInternalLocked(), token: " + token);
127b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
128b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
129b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        // Close an existing RemoteBridge
130b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        UinputBridge inputBridge = mBridgeMap.get(token);
131b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (inputBridge != null) {
132b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            inputBridge.close(token);
133b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
134b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
135b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        mBridgeMap.remove(token);
136b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
137b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
138b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
139b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void clearInputBridgeInternalLocked(IBinder token) {
140b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (DEBUG) {
141b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            Slog.d(TAG, "clearInputBridgeInternalLocked(), token: " + token);
142b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
143b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
144b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        UinputBridge inputBridge = mBridgeMap.get(token);
145b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (inputBridge != null) {
146b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            inputBridge.clear(token);
147b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
148b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
149b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
150b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void sendTimeStampInternalLocked(IBinder token, long timestamp) {
151b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        UinputBridge inputBridge = mBridgeMap.get(token);
152b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (inputBridge != null) {
153b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            inputBridge.sendTimestamp(token, timestamp);
154b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
155b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
156b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
157b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void sendKeyDownInternalLocked(IBinder token, int keyCode) {
158b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (DEBUG_KEYS) {
159b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            Slog.d(TAG, "sendKeyDownInternalLocked(), token: " + token + ", keyCode: " + keyCode);
160b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
161b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
162b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        UinputBridge inputBridge = mBridgeMap.get(token);
163b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (inputBridge != null) {
164b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            inputBridge.sendKeyDown(token, keyCode);
165b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
166b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
167b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
168b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void sendKeyUpInternalLocked(IBinder token, int keyCode) {
169b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (DEBUG_KEYS) {
170b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            Slog.d(TAG, "sendKeyUpInternalLocked(), token: " + token + ", keyCode: " + keyCode);
171b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
172b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
173b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        UinputBridge inputBridge = mBridgeMap.get(token);
174b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (inputBridge != null) {
175b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            inputBridge.sendKeyUp(token, keyCode);
176b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
177b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
178b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
179b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void sendPointerDownInternalLocked(IBinder token, int pointerId, int x, int y) {
180b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (DEBUG_KEYS) {
181b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            Slog.d(TAG, "sendPointerDownInternalLocked(), token: " + token + ", pointerId: " +
182b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    pointerId + ", x: " + x + ", y: " + y);
183b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
184b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
185b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        UinputBridge inputBridge = mBridgeMap.get(token);
186b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (inputBridge != null) {
187b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            inputBridge.sendPointerDown(token, pointerId, x, y);
188b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
189b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
190b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
191b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void sendPointerUpInternalLocked(IBinder token, int pointerId) {
192b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (DEBUG_KEYS) {
193b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            Slog.d(TAG, "sendPointerUpInternalLocked(), token: " + token + ", pointerId: " +
194b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    pointerId);
195b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
196b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
197b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        UinputBridge inputBridge = mBridgeMap.get(token);
198b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (inputBridge != null) {
199b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            inputBridge.sendPointerUp(token, pointerId);
200b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
201b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
202b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
203b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private void sendPointerSyncInternalLocked(IBinder token) {
204b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (DEBUG_KEYS) {
205b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            Slog.d(TAG, "sendPointerSyncInternalLocked(), token: " + token);
206b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
207b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
208b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        UinputBridge inputBridge = mBridgeMap.get(token);
209b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        if (inputBridge != null) {
210b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            inputBridge.sendPointerSync(token);
211b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
212b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
213b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
214b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private final class UserHandler extends Handler {
215b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
216b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public static final int MSG_START = 1;
217b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public static final int MSG_INPUT_BRIDGE_CONNECTED = 2;
218b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
219b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        private final TvRemoteProviderWatcher mWatcher;
220b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        private boolean mRunning;
221b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
222b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public UserHandler(UserProvider provider, Context context) {
223b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            super(Looper.getMainLooper(), null, true);
224b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            mWatcher = new TvRemoteProviderWatcher(context, provider, this);
225b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
226b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
227b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
228b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void handleMessage(Message msg) {
229b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            switch (msg.what) {
230b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                case MSG_START: {
231b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    start();
232b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    break;
233b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
234b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                case MSG_INPUT_BRIDGE_CONNECTED: {
235b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    IBinder token = (IBinder) msg.obj;
236b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    TvRemoteProviderProxy provider = mProviderMap.get(token);
237b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    if (provider != null) {
238b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                        provider.inputBridgeConnected(token);
239b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    }
240b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    break;
241b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
242b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
243b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
244b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
245b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        private void start() {
246b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (!mRunning) {
247b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                mRunning = true;
248b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                mWatcher.start(); // also starts all providers
249b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
250b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
251b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
252b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
253b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    private final class UserProvider implements TvRemoteProviderWatcher.ProviderMethods,
254b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            TvRemoteProviderProxy.ProviderMethods {
255b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
256b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        private final TvRemoteService mService;
257b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
258b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public UserProvider(TvRemoteService service) {
259b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            mService = service;
260b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
261b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
262b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
263b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
264b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                                    int width, int height, int maxPointers) {
265b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG) {
266b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                Slog.d(TAG, "openInputBridge(), token: " + token +
267b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                        ", name: " + name + ", width: " + width +
268b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                        ", height: " + height + ", maxPointers: " + maxPointers);
269b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
270b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
271b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
272b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.contains(provider)) {
273b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mService.openInputBridgeInternalLocked(provider, token, name, width, height,
274b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                            maxPointers);
275b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
276b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
277b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
278b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
279b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
280b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void closeInputBridge(TvRemoteProviderProxy provider, IBinder token) {
281b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG) Slog.d(TAG, "closeInputBridge(), token: " + token);
282b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
283b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.contains(provider)) {
284b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mService.closeInputBridgeInternalLocked(token);
285b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mProviderMap.remove(token);
286b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
287b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
288b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
289b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
290b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
291b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void clearInputBridge(TvRemoteProviderProxy provider, IBinder token) {
292b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG) Slog.d(TAG, "clearInputBridge(), token: " + token);
293b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
294b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.contains(provider)) {
295b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mService.clearInputBridgeInternalLocked(token);
296b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
297b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
298b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
299b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
300b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
301b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void sendTimeStamp(TvRemoteProviderProxy provider, IBinder token, long timestamp) {
302b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
303b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.contains(provider)) {
304b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mService.sendTimeStampInternalLocked(token, timestamp);
305b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
306b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
307b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
308b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
309b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
310b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void sendKeyDown(TvRemoteProviderProxy provider, IBinder token, int keyCode) {
311b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG_KEYS) {
312b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                Slog.d(TAG, "sendKeyDown(), token: " + token + ", keyCode: " + keyCode);
313b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
314b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
315b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.contains(provider)) {
316b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mService.sendKeyDownInternalLocked(token, keyCode);
317b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
318b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
319b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
320b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
321b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
322b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void sendKeyUp(TvRemoteProviderProxy provider, IBinder token, int keyCode) {
323b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG_KEYS) {
324b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                Slog.d(TAG, "sendKeyUp(), token: " + token + ", keyCode: " + keyCode);
325b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
326b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
327b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.contains(provider)) {
328b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mService.sendKeyUpInternalLocked(token, keyCode);
329b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
330b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
331b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
332b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
333b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
334b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void sendPointerDown(TvRemoteProviderProxy provider, IBinder token, int pointerId,
335b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                                    int x, int y) {
336b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG_KEYS) {
337b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                Slog.d(TAG, "sendPointerDown(), token: " + token + ", pointerId: " + pointerId);
338b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
339b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
340b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.contains(provider)) {
341b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mService.sendPointerDownInternalLocked(token, pointerId, x, y);
342b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
343b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
344b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
345b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
346b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
347b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void sendPointerUp(TvRemoteProviderProxy provider, IBinder token, int pointerId) {
348b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG_KEYS) {
349b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                Slog.d(TAG, "sendPointerUp(), token: " + token + ", pointerId: " + pointerId);
350b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
351b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
352b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.contains(provider)) {
353b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mService.sendPointerUpInternalLocked(token, pointerId);
354b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
355b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
356b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
357b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
358b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
359b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void sendPointerSync(TvRemoteProviderProxy provider, IBinder token) {
360b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG_KEYS) Slog.d(TAG, "sendPointerSync(), token: " + token);
361b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
362b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.contains(provider)) {
363b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    mService.sendPointerSyncInternalLocked(token);
364b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
365b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
366b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
367b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
368b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
369b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void addProvider(TvRemoteProviderProxy provider) {
370b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG) Slog.d(TAG, "addProvider " + provider);
371b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
372b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                provider.setProviderSink(this);
373b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                mProviderList.add(provider);
374b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                Slog.d(TAG, "provider: " + provider.toString());
375b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
376b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
377b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan
378b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        @Override
379b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        public void removeProvider(TvRemoteProviderProxy provider) {
380b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            if (DEBUG) Slog.d(TAG, "removeProvider " + provider);
381b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            synchronized (mLock) {
382b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                if (mProviderList.remove(provider) == false) {
383b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                    Slog.e(TAG, "Unknown provider " + provider);
384b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan                }
385b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan            }
386b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan        }
387b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan    }
388b5b86c11008422ac4bf5af5fed736f04ebbaa858Sujith Ramakrishnan}
389