diff options
| -rw-r--r-- | services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java | 216 |
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; + } + } + } + }); } } |