1/*
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 */
16package android.hardware.location;
17
18import android.annotation.SystemApi;
19import android.content.Context;
20import android.os.Handler;
21import android.os.IBinder;
22import android.os.Looper;
23import android.os.RemoteException;
24import android.os.ServiceManager;
25import android.util.Log;
26
27/**
28 * A class that exposes the Context hubs on a device to applications.
29 *
30 * Please note that this class is not expected to be used by unbundled applications. Also, calling
31 * applications are expected to have LOCATION_HARDWARE permissions to use this class.
32 *
33 * @hide
34 */
35@SystemApi
36public final class ContextHubManager {
37
38    private static final String TAG = "ContextHubManager";
39
40    private final Looper mMainLooper;
41    private IContextHubService mContextHubService;
42    private Callback mCallback;
43    private Handler mCallbackHandler;
44
45    /**
46     * @deprecated Use {@code mCallback} instead.
47     */
48    @Deprecated
49    private ICallback mLocalCallback;
50
51    /**
52     * An interface to receive asynchronous communication from the context hub.
53     */
54    public abstract static class Callback {
55        protected Callback() {}
56
57        /**
58         * Callback function called on message receipt from context hub.
59         *
60         * @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
61         * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
62         * @param message The context hub message.
63         *
64         * @see ContextHubMessage
65         */
66        public abstract void onMessageReceipt(
67                int hubHandle,
68                int nanoAppHandle,
69                ContextHubMessage message);
70    }
71
72    /**
73     * @deprecated Use {@link Callback} instead.
74     * @hide
75     */
76    @Deprecated
77    public interface ICallback {
78        /**
79         * Callback function called on message receipt from context hub.
80         *
81         * @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
82         * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
83         * @param message The context hub message.
84         *
85         * @see ContextHubMessage
86         */
87        void onMessageReceipt(int hubHandle, int nanoAppHandle, ContextHubMessage message);
88    }
89
90    /**
91     * Get a handle to all the context hubs in the system
92     * @return array of context hub handles
93     */
94    public int[] getContextHubHandles() {
95        int[] retVal = null;
96        try {
97            retVal = getBinder().getContextHubHandles();
98        } catch (RemoteException e) {
99            Log.w(TAG, "Could not fetch context hub handles : " + e);
100        }
101        return retVal;
102    }
103
104    /**
105     * Get more information about a specific hub.
106     *
107     * @param hubHandle Handle (system-wide unique identifier) of a context hub.
108     * @return ContextHubInfo Information about the requested context hub.
109     *
110     * @see ContextHubInfo
111     */
112    public ContextHubInfo getContextHubInfo(int hubHandle) {
113        ContextHubInfo retVal = null;
114        try {
115            retVal = getBinder().getContextHubInfo(hubHandle);
116        } catch (RemoteException e) {
117            Log.w(TAG, "Could not fetch context hub info :" + e);
118        }
119
120        return retVal;
121    }
122
123    /**
124     * Load a nano app on a specified context hub.
125     *
126     * @param hubHandle handle of context hub to load the app on.
127     * @param app the nanoApp to load on the hub
128     *
129     * @return int nanoAppInstance of the loaded nanoApp on success,
130     *         -1 otherwise
131     *
132     * @see NanoApp
133     */
134    public int loadNanoApp(int hubHandle, NanoApp app) {
135        int retVal = -1;
136
137        if (app == null) {
138            return retVal;
139        }
140
141        try {
142            retVal = getBinder().loadNanoApp(hubHandle, app);
143        } catch (RemoteException e) {
144            Log.w(TAG, "Could not load nanoApp :" + e);
145        }
146
147        return retVal;
148    }
149
150    /**
151     * Unload a specified nanoApp
152     *
153     * @param nanoAppHandle handle of the nanoApp to load
154     *
155     * @return int  0 on success, -1 otherwise
156     */
157    public int unloadNanoApp(int nanoAppHandle) {
158        int retVal = -1;
159
160        try {
161            retVal = getBinder().unloadNanoApp(nanoAppHandle);
162        } catch (RemoteException e) {
163            Log.w(TAG, "Could not fetch unload nanoApp :" + e);
164        }
165
166        return retVal;
167    }
168
169    /**
170     * get information about the nano app instance
171     *
172     * @param nanoAppHandle handle of the nanoAppInstance
173     * @return NanoAppInstanceInfo Information about the nano app instance.
174     *
175     * @see NanoAppInstanceInfo
176     */
177    public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
178        NanoAppInstanceInfo retVal = null;
179
180        try {
181            retVal = getBinder().getNanoAppInstanceInfo(nanoAppHandle);
182        } catch (RemoteException e) {
183            Log.w(TAG, "Could not fetch nanoApp info :" + e);
184        }
185
186        return retVal;
187    }
188
189    /**
190     * Find a specified nano app on the system
191     *
192     * @param hubHandle handle of hub to search for nano app
193     * @param filter filter specifying the search criteria for app
194     *
195     * @see NanoAppFilter
196     *
197     * @return int[] Array of handles to any found nano apps
198     */
199    public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) {
200        int[] retVal = null;
201        try {
202            retVal = getBinder().findNanoAppOnHub(hubHandle, filter);
203        } catch (RemoteException e) {
204            Log.w(TAG, "Could not query nanoApp instance :" + e);
205        }
206        return retVal;
207    }
208
209    /**
210     * Send a message to a specific nano app instance on a context hub.
211     *
212     * @param hubHandle handle of the hub to send the message to
213     * @param nanoAppHandle  handle of the nano app to send to
214     * @param message Message to be sent
215     *
216     * @see ContextHubMessage
217     *
218     * @return int 0 on success, -1 otherwise
219     */
220    public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) {
221        int retVal = -1;
222
223        if (message == null || message.getData() == null) {
224            Log.w(TAG, "null ptr");
225            return retVal;
226        }
227        try {
228            retVal = getBinder().sendMessage(hubHandle, nanoAppHandle, message);
229        } catch (RemoteException e) {
230            Log.w(TAG, "Could not send message :" + e.toString());
231        }
232
233        return retVal;
234    }
235
236    /**
237     * Set a callback to receive messages from the context hub
238     *
239     * @param callback Callback object
240     *
241     * @see Callback
242     *
243     * @return int 0 on success, -1 otherwise
244     */
245    public int registerCallback(Callback callback) {
246        return registerCallback(callback, null);
247    }
248
249    /**
250     * @deprecated Use {@link #registerCallback(Callback)} instead.
251     * @hide
252     */
253    @Deprecated
254    public int registerCallback(ICallback callback) {
255        if (mLocalCallback != null) {
256            Log.w(TAG, "Max number of local callbacks reached!");
257            return -1;
258        }
259        mLocalCallback = callback;
260        return 0;
261    }
262
263    /**
264     * Set a callback to receive messages from the context hub
265     *
266     * @param callback Callback object
267     * @param handler Handler object
268     *
269     * @see Callback
270     *
271     * @return int 0 on success, -1 otherwise
272     */
273    public int registerCallback(Callback callback, Handler handler) {
274        synchronized(this) {
275            if (mCallback != null) {
276                Log.w(TAG, "Max number of callbacks reached!");
277                return -1;
278            }
279            mCallback = callback;
280            mCallbackHandler = handler;
281        }
282        return 0;
283    }
284
285    /**
286     * Unregister a callback for receive messages from the context hub.
287     *
288     * @see Callback
289     *
290     * @param callback method to deregister
291     *
292     * @return int 0 on success, -1 otherwise
293     */
294    public int unregisterCallback(Callback callback) {
295      synchronized(this) {
296          if (callback != mCallback) {
297              Log.w(TAG, "Cannot recognize callback!");
298              return -1;
299          }
300
301          mCallback = null;
302          mCallbackHandler = null;
303      }
304      return 0;
305    }
306
307    /**
308     * @deprecated Use {@link #unregisterCallback(Callback)} instead.
309     * @hide
310     */
311    public synchronized int unregisterCallback(ICallback callback) {
312        if (callback != mLocalCallback) {
313            Log.w(TAG, "Cannot recognize local callback!");
314            return -1;
315        }
316        mLocalCallback = null;
317        return 0;
318    }
319
320    private IContextHubCallback.Stub mClientCallback = new IContextHubCallback.Stub() {
321        @Override
322        public void onMessageReceipt(final int hubId, final int nanoAppId,
323                final ContextHubMessage message) {
324            if (mCallback != null) {
325                synchronized(this) {
326                    final Callback callback = mCallback;
327                    Handler handler = mCallbackHandler == null ?
328                            new Handler(mMainLooper) : mCallbackHandler;
329                    handler.post(new Runnable() {
330                        @Override
331                        public void run() {
332                            callback.onMessageReceipt(hubId, nanoAppId, message);
333                        }
334                    });
335                }
336            } else if (mLocalCallback != null) {
337                // we always ensure that mCallback takes precedence, because mLocalCallback is only
338                // for internal compatibility
339                synchronized (this) {
340                    mLocalCallback.onMessageReceipt(hubId, nanoAppId, message);
341                }
342            } else {
343                Log.d(TAG, "Context hub manager client callback is NULL");
344            }
345        }
346    };
347
348    /** @hide */
349    public ContextHubManager(Context context, Looper mainLooper) {
350        mMainLooper = mainLooper;
351
352        IBinder b = ServiceManager.getService(ContextHubService.CONTEXTHUB_SERVICE);
353        if (b != null) {
354            mContextHubService = IContextHubService.Stub.asInterface(b);
355
356            try {
357                getBinder().registerCallback(mClientCallback);
358            } catch (RemoteException e) {
359                Log.w(TAG, "Could not register callback:" + e);
360            }
361
362        } else {
363            Log.w(TAG, "failed to getService");
364        }
365    }
366
367    private IContextHubService getBinder() throws RemoteException {
368        if (mContextHubService == null) {
369            throw new RemoteException("Service not connected.");
370        }
371        return mContextHubService;
372    }
373}
374