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