public class

CallbackHandlerRegistry

extends java.lang.Object

 java.lang.Object

↳androidx.remotecallback.CallbackHandlerRegistry

Gradle dependencies

compile group: 'androidx.remotecallback', name: 'remotecallback', version: '1.0.0-alpha02'

  • groupId: androidx.remotecallback
  • artifactId: remotecallback
  • version: 1.0.0-alpha02

Artifact androidx.remotecallback:remotecallback:1.0.0-alpha02 it located at Google repository (https://maven.google.com/)

Overview

The holder for callbacks that are tagged with RemoteCallable. Note: This should only be referenced by generated code, there is no reason to reference this otherwise.

Summary

Fields
public static final CallbackHandlerRegistrysInstance

Constructors
publicCallbackHandlerRegistry()

Methods
public CallbackReceiver<T>getAndResetStub(java.lang.Class<CallbackReceiver> cls, Context context, java.lang.String authority)

public voidinvokeCallback(Context context, CallbackReceiver<T> receiver, Bundle bundle)

Trigger a call to a callback using arguments that were generated with RemoteCallback.getArgumentBundle().

public voidinvokeCallback(Context context, CallbackReceiver<T> receiver, Intent intent)

Trigger a call to a callback using arguments that were generated with RemoteCallback.getArgumentBundle().

public static voidregisterCallbackHandler(java.lang.Class<CallbackReceiver> cls, java.lang.String method, CallbackHandlerRegistry.CallbackHandler<CallbackReceiver> handler)

Registers a callback handler to be executed when a given PendingIntent is fired for a RemoteCallback.

public static RemoteCallbackstubToRemoteCallback(CallbackReceiver receiver, java.lang.Class<CallbackReceiver> cls, Bundle args, java.lang.String method)

Turns a callback receiver stub into a remote callback.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final CallbackHandlerRegistry sInstance

Constructors

public CallbackHandlerRegistry()

Methods

public CallbackReceiver<T> getAndResetStub(java.lang.Class<CallbackReceiver> cls, Context context, java.lang.String authority)

public void invokeCallback(Context context, CallbackReceiver<T> receiver, Intent intent)

Trigger a call to a callback using arguments that were generated with RemoteCallback.getArgumentBundle().

public void invokeCallback(Context context, CallbackReceiver<T> receiver, Bundle bundle)

Trigger a call to a callback using arguments that were generated with RemoteCallback.getArgumentBundle().

public static void registerCallbackHandler(java.lang.Class<CallbackReceiver> cls, java.lang.String method, CallbackHandlerRegistry.CallbackHandler<CallbackReceiver> handler)

Registers a callback handler to be executed when a given PendingIntent is fired for a RemoteCallback. Note: This should only be called by generated code, there is no reason to reference this otherwise.

public static RemoteCallback stubToRemoteCallback(CallbackReceiver receiver, java.lang.Class<CallbackReceiver> cls, Bundle args, java.lang.String method)

Turns a callback receiver stub into a remote callback. Note: This should only be called by generated code, there is no reason to reference this otherwise.

Source

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.remotecallback;

import static androidx.remotecallback.RemoteCallback.EXTRA_METHOD;

import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.RestrictTo;
import androidx.collection.ArrayMap;

import java.lang.reflect.InvocationTargetException;

/**
 * The holder for callbacks that are tagged with {@link RemoteCallable}.
 * Note: This should only be referenced by generated code, there is no reason to reference this
 * otherwise.
 */
public class CallbackHandlerRegistry {
    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
    public static final CallbackHandlerRegistry sInstance = new CallbackHandlerRegistry();
    private static final String TAG = "CallbackHandlerRegistry";

    private final ArrayMap<Class<? extends CallbackReceiver>, ClsHandler> mClsLookup =
            new ArrayMap<>();

    /**
     * @hide
     */
    @SuppressWarnings("TypeParameterUnusedInFormals")
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
    public <T extends CallbackReceiver> T getAndResetStub(Class<? extends CallbackReceiver> cls,
            Context context, String authority) {
        ensureInitialized(cls);
        ClsHandler stub = findMap(cls);
        initStub(stub.mCallStub, cls, context, authority);
        return (T) stub.mCallStub;
    }

    private void initStub(CallbackReceiver stub, Class<? extends CallbackReceiver> cls,
            Context context, String authority) {
        ClsHandler clsHandler = findMap(cls);
        clsHandler.mContext = context;
        if (stub instanceof ContentProvider) {
            clsHandler.mAuthority = determineAuthority(context, authority, cls);
        } else {
            clsHandler.mAuthority = null;
        }
    }

    private String determineAuthority(Context context, String authority, Class<?> aClass) {
        if (authority != null) {
            return authority;
        }
        try {
            ProviderInfo info = context.getPackageManager().getProviderInfo(
                    new ComponentName(context.getPackageName(), aClass.getName()),
                    0);
            return info.authority;
        } catch (PackageManager.NameNotFoundException e) {
            Log.w(TAG, "Couldn't find provider " + aClass, e);
            return null;
        }
    }

    <T extends CallbackReceiver> void ensureInitialized(Class<T> cls) {
        synchronized (this) {
            if (!mClsLookup.containsKey(cls)) {
                runInit(cls);
            }
        }
    }

    /**
     * Trigger a call to a callback using arguments that were generated with
     * {@link RemoteCallback#getArgumentBundle()}.
     */
    public <T extends CallbackReceiver> void invokeCallback(Context context, T receiver,
            Intent intent) {
        invokeCallback(context, receiver, intent.getExtras());
    }

    /**
     * Trigger a call to a callback using arguments that were generated with
     * {@link RemoteCallback#getArgumentBundle()}.
     */
    public <T extends CallbackReceiver> void invokeCallback(Context context, T receiver,
            Bundle bundle) {
        Class<? extends CallbackReceiver> receiverClass = receiver.getClass();
        ensureInitialized(receiverClass);
        ClsHandler map = findMap(receiverClass);
        if (map == null) {
            Log.e(TAG, "No map found for " + receiverClass.getName());
            return;
        }
        String method = bundle.getString(EXTRA_METHOD);
        @SuppressWarnings("unchecked")
        CallbackHandler<T> callbackHandler = (CallbackHandler<T>) map.mHandlers.get(method);
        if (callbackHandler == null) {
            Log.e(TAG, "No handler found for " + method + " on " + receiverClass.getName());
            return;
        }
        callbackHandler.executeCallback(context, receiver, bundle);
    }

    private ClsHandler findMap(Class<?> aClass) {
        ClsHandler map;
        synchronized (this) {
            map = mClsLookup.get(aClass);
        }
        if (map != null) {
            return map;
        }
        if (aClass.getSuperclass() != null) {
            return findMap(aClass.getSuperclass());
        }
        return null;
    }

    private <T extends CallbackReceiver> void runInit(Class<T> cls) {
        try {
            // This is the only bit of reflection/keeping that needs to exist, one init class
            // per callback receiver.
            ClsHandler clsHandler = new ClsHandler();
            mClsLookup.put(cls, clsHandler);
            clsHandler.mCallStub =
                    (CallbackReceiver) findInitClass(cls).getDeclaredConstructor().newInstance();
        } catch (InstantiationException e) {
            Log.e(TAG, "Unable to initialize " + cls.getName(), e);
        } catch (IllegalAccessException e) {
            Log.e(TAG, "Unable to initialize " + cls.getName(), e);
        } catch (InvocationTargetException e) {
            Log.e(TAG, "Unable to initialize " + cls.getName(), e);
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "Unable to initialize " + cls.getName(), e);
        } catch (ClassNotFoundException e) {
            Log.e(TAG, "Unable to initialize " + cls.getName(), e);
        }
    }

    private <T extends CallbackReceiver> void registerHandler(Class<T> cls, String method,
            CallbackHandler<T> handler) {
        ClsHandler map = mClsLookup.get(cls);
        if (map == null) {
            throw new IllegalStateException("registerHandler called before init was run");
        }
        map.mHandlers.put(method, handler);
    }

    @SuppressWarnings("unchecked")
    private static Class<? extends Runnable> findInitClass(Class<? extends CallbackReceiver> cls)
            throws ClassNotFoundException {
        String pkg = cls.getPackage().getName();
        String c = String.format("%s.%sInitializer", pkg, cls.getSimpleName());
        return (Class<? extends Runnable>) Class.forName(c, false, cls.getClassLoader());
    }

    /**
     * Registers a callback handler to be executed when a given PendingIntent is fired
     * for a {@link RemoteCallback}.
     * Note: This should only be called by generated code, there is no reason to reference this
     * otherwise.
     */
    public static <T extends CallbackReceiver> void registerCallbackHandler(Class<T> cls,
            String method, CallbackHandler<T> handler) {
        sInstance.registerHandler(cls, method, handler);
    }

    /**
     * Turns a callback receiver stub into a remote callback.
     * Note: This should only be called by generated code, there is no reason to reference this
     * otherwise.
     */
    public static RemoteCallback stubToRemoteCallback(CallbackReceiver receiver,
            Class<? extends CallbackReceiver> cls, Bundle args, String method) {
        if (!(receiver instanceof CallbackBase)) {
            throw new IllegalArgumentException(
                    "May only be called on classes that extend a *WithCallbacks base class.");
        }
        ClsHandler clsHandler = sInstance.findMap(cls);
        Context context = clsHandler.mContext;
        String authority = clsHandler.mAuthority;
        // Clear out context and authority to avoid context leak.
        clsHandler.mContext = null;
        clsHandler.mAuthority = null;
        return ((CallbackBase) receiver).toRemoteCallback(cls, context, authority, args, method);
    }

    static class ClsHandler {
        final ArrayMap<String, CallbackHandler<? extends CallbackReceiver>> mHandlers =
                new ArrayMap<>();
        public String mAuthority;
        Context mContext;
        CallbackReceiver mCallStub;
    }

    /**
     * The interface used to trigger a callback when the pending intent is fired.
     * Note: This should only be referenced by generated code, there is no reason to reference
     * this otherwise.
     *
     * @param <T> The receiver type for this callback handler.
     */
    public interface CallbackHandler<T extends CallbackReceiver> {
        /**
         * Executes a callback given a Bundle of aurgements.
         * Note: This should only be called by generated code, there is no reason to reference this
         * otherwise.
         */
        void executeCallback(Context context, T receiver, Bundle arguments);
    }
}