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