summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java216
1 files changed, 169 insertions, 47 deletions
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index c040ad02282d..3cb3431e906b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -44,11 +44,15 @@ import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
import android.os.Process;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.location.ClientBrokerProto;
import java.util.Collections;
@@ -127,6 +131,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
private static final long CHANGE_ID_AUTH_STATE_DENIED = 181350407L;
+ private static final long WAKELOCK_TIMEOUT_MILLIS = 5 * 1000;
/*
* The context of the service.
@@ -157,7 +162,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* The remote callback interface for this client. This will be set to null whenever the
* client connection is closed (either explicitly or via binder death).
*/
- private IContextHubClientCallback mCallbackInterface = null;
+ private IContextHubClientCallback mCallbackInterface;
/*
* True if the client is still registered with the Context Hub Service, false otherwise.
@@ -165,10 +170,21 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
private boolean mRegistered = true;
/**
- * String containing an attribution tag that was denoted in the {@link Context} of the
- * creator of this broker. This is used when attributing the permissions usage of the broker.
+ * String containing an attribution tag that was denoted in the {@link Context} of the creator
+ * of this broker. This is used when attributing the permissions usage of the broker.
*/
- private @Nullable String mAttributionTag;
+ @Nullable private String mAttributionTag;
+
+ /** Wakelock held while nanoapp message are in flight to the client */
+ @GuardedBy("mWakeLock")
+ private final WakeLock mWakeLock;
+
+ /**
+ * True if {@link #mWakeLock} is open for acquisition. It is set to false after the client is
+ * unregistered.
+ */
+ @GuardedBy("mWakeLock")
+ private boolean mIsWakeLockActive = true;
/*
* Internal interface used to invoke client callbacks.
@@ -210,13 +226,13 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
/*
* True if a PendingIntent has been cancelled.
*/
- private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
+ private final AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
/**
* True if a permissions query has been issued and is being processed. Used to prevent too many
* queries from being issued by a single client at once.
*/
- private AtomicBoolean mIsPermQueryIssued = new AtomicBoolean(false);
+ private final AtomicBoolean mIsPermQueryIssued = new AtomicBoolean(false);
/*
* Map containing all nanoapps this client has a messaging channel with and whether it is
@@ -266,7 +282,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
/*
* Helper class to manage registered PendingIntent requests from the client.
*/
- private class PendingIntentRequest {
+ private static class PendingIntentRequest {
/*
* The PendingIntent object to request, null if there is no open request.
*/
@@ -344,27 +360,60 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
mUid = Binder.getCallingUid();
mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ PowerManager powerManager = context.getSystemService(PowerManager.class);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock.setWorkSource(new WorkSource(mUid, mPackage));
+ mWakeLock.setReferenceCounted(true);
+
startMonitoringOpChanges();
sendHostEndpointConnectedEvent();
}
/* package */ ContextHubClientBroker(
- Context context, IContextHubWrapper contextHubProxy,
- ContextHubClientManager clientManager, ContextHubInfo contextHubInfo,
- short hostEndPointId, IContextHubClientCallback callback, String attributionTag,
- ContextHubTransactionManager transactionManager, String packageName) {
- this(context, contextHubProxy, clientManager, contextHubInfo, hostEndPointId, callback,
- attributionTag, transactionManager, null /* pendingIntent */, 0 /* nanoAppId */,
+ Context context,
+ IContextHubWrapper contextHubProxy,
+ ContextHubClientManager clientManager,
+ ContextHubInfo contextHubInfo,
+ short hostEndPointId,
+ IContextHubClientCallback callback,
+ String attributionTag,
+ ContextHubTransactionManager transactionManager,
+ String packageName) {
+ this(
+ context,
+ contextHubProxy,
+ clientManager,
+ contextHubInfo,
+ hostEndPointId,
+ callback,
+ attributionTag,
+ transactionManager,
+ /* pendingIntent= */ null,
+ /* nanoAppId= */ 0,
packageName);
}
/* package */ ContextHubClientBroker(
- Context context, IContextHubWrapper contextHubProxy,
- ContextHubClientManager clientManager, ContextHubInfo contextHubInfo,
- short hostEndPointId, PendingIntent pendingIntent, long nanoAppId,
- String attributionTag, ContextHubTransactionManager transactionManager) {
- this(context, contextHubProxy, clientManager, contextHubInfo, hostEndPointId,
- null /* callback */, attributionTag, transactionManager, pendingIntent, nanoAppId,
+ Context context,
+ IContextHubWrapper contextHubProxy,
+ ContextHubClientManager clientManager,
+ ContextHubInfo contextHubInfo,
+ short hostEndPointId,
+ PendingIntent pendingIntent,
+ long nanoAppId,
+ String attributionTag,
+ ContextHubTransactionManager transactionManager) {
+ this(
+ context,
+ contextHubProxy,
+ clientManager,
+ contextHubInfo,
+ hostEndPointId,
+ /* callback= */ null,
+ attributionTag,
+ transactionManager,
+ pendingIntent,
+ nanoAppId,
pendingIntent.getCreatorPackage());
}
@@ -560,7 +609,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* Notifies the client of a hub reset event if the connection is open.
*/
/* package */ void onHubReset() {
- invokeCallback(callback -> callback.onHubReset());
+ invokeCallback(IContextHubClientCallback::onHubReset);
sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_HUB_RESET));
// Re-send the host endpoint connected event as the Context Hub restarted.
@@ -588,7 +637,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* @return true if the given PendingIntent is currently registered, false otherwise
*/
/* package */ boolean hasPendingIntent(PendingIntent intent, long nanoAppId) {
- PendingIntent pendingIntent = null;
+ PendingIntent pendingIntent;
long intentNanoAppId;
synchronized (this) {
pendingIntent = mPendingIntentRequest.getPendingIntent();
@@ -634,7 +683,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*/
/* package */ boolean notePermissions(List<String> permissions, String noteMessage) {
for (String permission : permissions) {
- int opCode = mAppOpsManager.permissionToOpCode(permission);
+ int opCode = AppOpsManager.permissionToOpCode(permission);
if (opCode != AppOpsManager.OP_NONE) {
try {
if (mAppOpsManager.noteOp(opCode, mUid, mPackage, mAttributionTag, noteMessage)
@@ -787,6 +836,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
private synchronized void invokeCallback(CallbackConsumer consumer) {
if (mCallbackInterface != null) {
try {
+ acquireWakeLock();
consumer.accept(mCallbackInterface);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while invoking client callback (host endpoint ID = "
@@ -850,19 +900,29 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* Sends a PendingIntent with extra Intent data
*
* @param pendingIntent the PendingIntent
- * @param intent the extra Intent data
+ * @param intent the extra Intent data
*/
private void doSendPendingIntent(PendingIntent pendingIntent, Intent intent) {
try {
String requiredPermission = Manifest.permission.ACCESS_CONTEXT_HUB;
+ acquireWakeLock();
pendingIntent.send(
- mContext, 0 /* code */, intent, null /* onFinished */, null /* Handler */,
- requiredPermission, null /* options */);
+ mContext,
+ /* code= */ 0,
+ intent,
+ /* onFinished= */ this,
+ /* handler= */ null,
+ requiredPermission,
+ /* options= */ null);
} catch (PendingIntent.CanceledException e) {
mIsPendingIntentCancelled.set(true);
// The PendingIntent is no longer valid
- Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
- + " (host endpoint ID " + mHostEndPointId + ")");
+ Log.w(
+ TAG,
+ "PendingIntent has been canceled, unregistering from client"
+ + " (host endpoint ID "
+ + mHostEndPointId
+ + ")");
close();
}
}
@@ -882,13 +942,14 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
mCallbackInterface.asBinder().unlinkToDeath(this, 0 /* flags */);
mCallbackInterface = null;
}
+ // The client is only unregistered and cleared when there is NOT any PendingIntent
if (!mPendingIntentRequest.hasPendingIntent() && mRegistered) {
mClientManager.unregisterClient(mHostEndPointId);
mRegistered = false;
+ mAppOpsManager.stopWatchingMode(this);
+ mContextHubProxy.onHostEndpointDisconnected(mHostEndPointId);
+ releaseWakeLockOnExit();
}
- mAppOpsManager.stopWatchingMode(this);
-
- mContextHubProxy.onHostEndpointDisconnected(mHostEndPointId);
}
private String authStateToString(@ContextHubManager.AuthorizationState int state) {
@@ -939,41 +1000,46 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
@Override
public String toString() {
- String out = "[ContextHubClient ";
- out += "endpointID: " + getHostEndPointId() + ", ";
- out += "contextHub: " + getAttachedContextHubId() + ", ";
+ StringBuilder out = new StringBuilder("[ContextHubClient ");
+ out.append("endpointID: ").append(getHostEndPointId()).append(", ");
+ out.append("contextHub: ").append(getAttachedContextHubId()).append(", ");
if (mAttributionTag != null) {
- out += "attributionTag: " + getAttributionTag() + ", ";
+ out.append("attributionTag: ").append(getAttributionTag()).append(", ");
}
if (mPendingIntentRequest.isValid()) {
- out += "intentCreatorPackage: " + mPackage + ", ";
- out += "nanoAppId: 0x" + Long.toHexString(mPendingIntentRequest.getNanoAppId());
+ out.append("intentCreatorPackage: ").append(mPackage).append(", ");
+ out.append("nanoAppId: 0x")
+ .append(Long.toHexString(mPendingIntentRequest.getNanoAppId()));
} else {
- out += "package: " + mPackage;
+ out.append("package: ").append(mPackage);
}
if (mMessageChannelNanoappIdMap.size() > 0) {
- out += " messageChannelNanoappSet: (";
+ out.append(" messageChannelNanoappSet: (");
Iterator<Map.Entry<Long, Integer>> it =
mMessageChannelNanoappIdMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Long, Integer> entry = it.next();
- out += "0x" + Long.toHexString(entry.getKey()) + " auth state: "
- + authStateToString(entry.getValue());
+ out.append("0x")
+ .append(Long.toHexString(entry.getKey()))
+ .append(" auth state: ")
+ .append(authStateToString(entry.getValue()));
if (it.hasNext()) {
- out += ",";
+ out.append(",");
}
}
- out += ")";
+ out.append(")");
}
- out += "]";
-
- return out;
+ synchronized (mWakeLock) {
+ out.append("wakelock: ").append(mWakeLock);
+ }
+ out.append("]");
+ return out.toString();
}
/** Callback that arrives when direct-call message callback delivery completed */
@Override
public void callbackFinished() {
- // TODO(b/202447392): pending implementation.
+ releaseWakeLock();
}
@Override
@@ -983,6 +1049,62 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
int resultCode,
String resultData,
Bundle resultExtras) {
- // TODO(b/202447392): pending implementation.
+ releaseWakeLock();
+ }
+
+ private void acquireWakeLock() {
+ Binder.withCleanCallingIdentity(
+ () -> {
+ synchronized (mWakeLock) {
+ if (mIsWakeLockActive) {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
+ }
+ }
+ });
+ }
+
+ /**
+ * Releases the wakelock.
+ *
+ * <p>The check-and-release operation should be atomic to avoid overly release.
+ */
+ private void releaseWakeLock() {
+ Binder.withCleanCallingIdentity(
+ () -> {
+ synchronized (mWakeLock) {
+ if (mWakeLock.isHeld()) {
+ try {
+ mWakeLock.release();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Releasing the wakelock fails - ", e);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Releases the wakelock for all the acquisitions during cleanup.
+ *
+ * <p>The check-and-release operation should be atomic to avoid overly release.
+ */
+ private void releaseWakeLockOnExit() {
+ Binder.withCleanCallingIdentity(
+ () -> {
+ synchronized (mWakeLock) {
+ mIsWakeLockActive = false;
+ while (mWakeLock.isHeld()) {
+ try {
+ mWakeLock.release();
+ } catch (RuntimeException e) {
+ Log.e(
+ TAG,
+ "Releasing the wakelock for all acquisitions fails - ",
+ e);
+ break;
+ }
+ }
+ }
+ });
}
}