Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4aec3307 authored by Lei Ju's avatar Lei Ju Committed by Android (Google) Code Review
Browse files

Merge changes I683ad404,I2abc5b20

* changes:
  Clean up the client when no PendingIntent exist
  Update ContextHubClientBroker to use a wakelock
parents 80a39174 d84c53b9
Loading
Loading
Loading
Loading
+169 −47
Original line number Diff line number Diff line
@@ -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 = "
@@ -855,14 +905,24 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
    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();
        }
    }

    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;
                            }
                        }
                    }
                });
    }
}