1 /*
2  * Copyright 2017 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 package android.hardware.location;
17 
18 import android.annotation.NonNull;
19 import android.annotation.RequiresPermission;
20 import android.annotation.SystemApi;
21 import android.app.PendingIntent;
22 import android.os.RemoteException;
23 import android.util.Log;
24 
25 import dalvik.system.CloseGuard;
26 
27 import java.io.Closeable;
28 import java.util.Objects;
29 import java.util.concurrent.atomic.AtomicBoolean;
30 
31 /**
32  * A class describing a client of the Context Hub Service.
33  *
34  * Clients can send messages to nanoapps at a Context Hub through this object. The APIs supported
35  * by this object are thread-safe and can be used without external synchronization.
36  *
37  * @hide
38  */
39 @SystemApi
40 public class ContextHubClient implements Closeable {
41     private static final String TAG = "ContextHubClient";
42 
43     /*
44      * The proxy to the client interface at the service.
45      */
46     private IContextHubClient mClientProxy = null;
47 
48     /*
49      * The Context Hub that this client is attached to.
50      */
51     private final ContextHubInfo mAttachedHub;
52 
53     private final CloseGuard mCloseGuard;
54 
55     private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
56 
57     /*
58      * True if this is a persistent client (i.e. does not have to close the connection when the
59      * resource is freed from the system).
60      */
61     private final boolean mPersistent;
62 
ContextHubClient(ContextHubInfo hubInfo, boolean persistent)63     /* package */ ContextHubClient(ContextHubInfo hubInfo, boolean persistent) {
64         mAttachedHub = hubInfo;
65         mPersistent = persistent;
66         if (mPersistent) {
67             mCloseGuard = null;
68         } else {
69             mCloseGuard = CloseGuard.get();
70             mCloseGuard.open("close");
71         }
72     }
73 
74     /**
75      * Sets the proxy interface of the client at the service. This method should always be called
76      * by the ContextHubManager after the client is registered at the service, and should only be
77      * called once.
78      *
79      * @param clientProxy the proxy of the client at the service
80      */
setClientProxy(IContextHubClient clientProxy)81     /* package */ void setClientProxy(IContextHubClient clientProxy) {
82         Objects.requireNonNull(clientProxy, "IContextHubClient cannot be null");
83         if (mClientProxy != null) {
84             throw new IllegalStateException("Cannot change client proxy multiple times");
85         }
86 
87         mClientProxy = clientProxy;
88     }
89 
90     /**
91      * Returns the hub that this client is attached to.
92      *
93      * @return the ContextHubInfo of the attached hub
94      */
95     @NonNull
getAttachedHub()96     public ContextHubInfo getAttachedHub() {
97         return mAttachedHub;
98     }
99 
100     /**
101      * Closes the connection for this client and the Context Hub Service.
102      *
103      * When this function is invoked, the messaging associated with this client is invalidated.
104      * All futures messages targeted for this client are dropped at the service, and the
105      * ContextHubClient is unregistered from the service.
106      *
107      * If this object has a PendingIntent, i.e. the object was generated via
108      * {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo, long)}, then the
109      * Intent events corresponding to the PendingIntent will no longer be triggered.
110      */
close()111     public void close() {
112         if (!mIsClosed.getAndSet(true)) {
113             if (mCloseGuard != null) {
114                 mCloseGuard.close();
115             }
116             try {
117                 mClientProxy.close();
118             } catch (RemoteException e) {
119                 throw e.rethrowFromSystemServer();
120             }
121         }
122     }
123 
124     /**
125      * Sends a message to a nanoapp through the Context Hub Service.
126      *
127      * This function returns RESULT_SUCCESS if the message has reached the HAL, but
128      * does not guarantee delivery of the message to the target nanoapp.
129      *
130      * @param message the message object to send
131      *
132      * @return the result of sending the message defined as in ContextHubTransaction.Result
133      *
134      * @throws NullPointerException if NanoAppMessage is null
135      *
136      * @see NanoAppMessage
137      * @see ContextHubTransaction.Result
138      */
139     @RequiresPermission(anyOf = {
140             android.Manifest.permission.LOCATION_HARDWARE,
141             android.Manifest.permission.ACCESS_CONTEXT_HUB
142     })
143     @ContextHubTransaction.Result
sendMessageToNanoApp(@onNull NanoAppMessage message)144     public int sendMessageToNanoApp(@NonNull NanoAppMessage message) {
145         Objects.requireNonNull(message, "NanoAppMessage cannot be null");
146 
147         int maxPayloadBytes = mAttachedHub.getMaxPacketLengthBytes();
148         byte[] payload = message.getMessageBody();
149         if (payload != null && payload.length > maxPayloadBytes) {
150             Log.e(TAG, "Message (" + payload.length + " bytes) exceeds max payload length ("
151                     + maxPayloadBytes + " bytes)");
152             return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
153         }
154 
155         try {
156             return mClientProxy.sendMessageToNanoApp(message);
157         } catch (RemoteException e) {
158             throw e.rethrowFromSystemServer();
159         }
160     }
161 
162     @Override
finalize()163     protected void finalize() throws Throwable {
164         try {
165             if (mCloseGuard != null) {
166                 mCloseGuard.warnIfOpen();
167             }
168             if (!mPersistent) {
169                 close();
170             }
171         } finally {
172             super.finalize();
173         }
174     }
175 }
176