@@ -0,0 +1,20 @@ | |||
gen/ | |||
bin/ | |||
Thumbs.db | |||
proguard/dump.txt | |||
proguard/mapping.txt | |||
proguard/seeds.txt | |||
proguard/usage.txt | |||
log.txt | |||
.idea/ | |||
*.iml | |||
.gradle/ | |||
build/ | |||
proguard/ | |||
local.properties | |||
gradle.properties | |||
gradle/ | |||
gradlew | |||
gradlew.bat |
@@ -0,0 +1 @@ | |||
/build |
@@ -0,0 +1,58 @@ | |||
apply plugin: 'com.jfrog.bintray' | |||
version = libraryVersion | |||
if (project.hasProperty("android")) { // Android libraries | |||
task sourcesJar(type: Jar) { | |||
classifier = 'sources' | |||
from android.sourceSets.main.java.srcDirs | |||
} | |||
task javadoc(type: Javadoc) { | |||
source = android.sourceSets.main.java.srcDirs | |||
classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) | |||
} | |||
} else { // Java libraries | |||
task sourcesJar(type: Jar, dependsOn: classes) { | |||
classifier = 'sources' | |||
from sourceSets.main.allSource | |||
} | |||
} | |||
task javadocJar(type: Jar, dependsOn: javadoc) { | |||
classifier = 'javadoc' | |||
from javadoc.destinationDir | |||
} | |||
artifacts { | |||
archives javadocJar | |||
archives sourcesJar | |||
} | |||
// Bintray | |||
Properties properties = new Properties() | |||
properties.load(project.rootProject.file('gradle.properties').newDataInputStream()) | |||
bintray { | |||
user = properties.getProperty("bintray.user") | |||
key = properties.getProperty("bintray.apikey") | |||
configurations = ['archives'] | |||
pkg { | |||
repo = bintrayRepo | |||
name = bintrayName | |||
desc = libraryDescription | |||
websiteUrl = siteUrl | |||
vcsUrl = gitUrl | |||
licenses = allLicenses | |||
publish = true | |||
publicDownloadNumbers = true | |||
version { | |||
desc = libraryDescription | |||
gpg { | |||
sign = true //Determines whether to GPG sign the files. The default is false | |||
passphrase = properties.getProperty("bintray.gpg.password") //Optional. The passphrase for GPG signing' | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
apply plugin: 'com.android.library' | |||
android { | |||
compileSdkVersion versions.compileSdk | |||
defaultConfig { | |||
minSdkVersion versions.minSdk | |||
targetSdkVersion versions.targetSdk | |||
versionCode 1 | |||
versionName "1.0" | |||
} | |||
buildTypes { | |||
release { | |||
minifyEnabled false | |||
consumerProguardFiles 'proguard-rules.pro' | |||
} | |||
} | |||
} | |||
dependencies { | |||
compileOnly deps.rx.java | |||
compileOnly deps.rx.java2 | |||
} | |||
apply from: 'properties.gradle' | |||
apply from: 'install.gradle' | |||
apply from: 'bintray.gradle' |
@@ -0,0 +1,44 @@ | |||
apply plugin: 'com.github.dcendents.android-maven' | |||
// Maven Group ID for the artifact | |||
group = publishedGroupId | |||
install { | |||
repositories.mavenInstaller { | |||
// This generates POM.xml with proper parameters | |||
pom { | |||
project { | |||
packaging 'aar' | |||
groupId publishedGroupId | |||
artifactId artifact | |||
// Add your description here | |||
name libraryName | |||
description libraryDescription | |||
url siteUrl | |||
// Set your license | |||
licenses { | |||
license { | |||
name licenseName | |||
url licenseUrl | |||
} | |||
} | |||
developers { | |||
developer { | |||
id developerId | |||
name developerName | |||
email developerEmail | |||
} | |||
} | |||
scm { | |||
connection gitUrl | |||
developerConnection gitUrl | |||
url siteUrl | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
# Add project specific ProGuard rules here. | |||
# You can control the set of applied configuration files using the | |||
# proguardFiles setting in build.gradle. | |||
# | |||
# For more details, see | |||
# http://developer.android.com/guide/developing/tools/proguard.html | |||
# If your project uses WebView with JS, uncomment the following | |||
# and specify the fully qualified class name to the JavaScript interface | |||
# class: | |||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | |||
# public *; | |||
#} | |||
# Uncomment this to preserve the line number information for | |||
# debugging stack traces. | |||
#-keepattributes SourceFile,LineNumberTable | |||
# If you keep the line number information, uncomment this to | |||
# hide the original source file name. | |||
#-renamesourcefileattribute SourceFile | |||
-dontwarn com.codezjx.andlinker.adapter.** | |||
-keep class * implements com.codezjx.andlinker.SuperParcelable { | |||
public void readFromParcel(android.os.Parcel); | |||
} | |||
-keep @com.codezjx.andlinker.annotation.RemoteInterface class * { | |||
<methods>; | |||
} |
@@ -0,0 +1,23 @@ | |||
ext { | |||
bintrayRepo = 'maven' | |||
bintrayName = 'and-linker' | |||
publishedGroupId = 'com.codezjx.library' | |||
libraryName = 'AndLinker' | |||
artifact = 'andlinker' | |||
libraryDescription = 'AndLinker is a IPC library for Android, which combines the features of AIDL and Retrofit. Allows IPC call seamlessly compose with rxjava and rxjava2 call adapters.' | |||
siteUrl = 'https://github.com/codezjx/AndLinker' | |||
gitUrl = 'https://github.com/codezjx/AndLinker.git' | |||
libraryVersion = '0.7.1' | |||
developerId = 'codezjx' | |||
developerName = 'codezjx' | |||
developerEmail = 'code.zjx@gmail.com' | |||
licenseName = 'The Apache Software License, Version 2.0' | |||
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' | |||
allLicenses = ["Apache-2.0"] | |||
} |
@@ -0,0 +1,2 @@ | |||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||
package="com.codezjx.andlinker"/> |
@@ -0,0 +1,295 @@ | |||
package com.codezjx.andlinker; | |||
import android.content.ComponentName; | |||
import android.content.Context; | |||
import android.content.Intent; | |||
import android.content.ServiceConnection; | |||
import android.os.IBinder; | |||
import android.os.RemoteException; | |||
import com.codezjx.andlinker.adapter.DefaultCallAdapterFactory; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.InvocationHandler; | |||
import java.lang.reflect.Method; | |||
import java.lang.reflect.Proxy; | |||
import java.lang.reflect.Type; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
import static com.codezjx.andlinker.Utils.checkNotNull; | |||
/** | |||
* AndLinker adapts a Java interface to IPC calls by using annotations on the declared methods to | |||
* define how requests are made. Create instances using {@linkplain Builder | |||
* the builder} and pass your interface to {@link #create} to generate an implementation. | |||
*/ | |||
public final class AndLinker { | |||
private static final String TAG = "AndLinker"; | |||
private final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>(); | |||
private ServiceConnection mServiceConnection; | |||
private Invoker mInvoker; | |||
private Context mContext; | |||
private String mPackageName; | |||
private String mAction; | |||
private String mClassName; | |||
private List<CallAdapter.Factory> mAdapterFactories; | |||
private Dispatcher mDispatcher; | |||
private ITransfer mTransferService; | |||
private ICallback mCallback; | |||
private BindCallback mBindCallback; | |||
private AndLinker(Context context, String packageName, String action, String className, List<CallAdapter.Factory> adapterFactories) { | |||
mContext = context; | |||
mPackageName = packageName; | |||
mAction = action; | |||
mClassName = className; | |||
mInvoker = new Invoker(); | |||
mAdapterFactories = adapterFactories; | |||
mDispatcher = new Dispatcher(); | |||
mServiceConnection = createServiceConnection(); | |||
mCallback = createCallback(); | |||
} | |||
/** | |||
* Create an implementation defined by the remote service interface. | |||
*/ | |||
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety. | |||
public <T> T create(final Class<T> service) { | |||
Utils.validateServiceInterface(service); | |||
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, | |||
new InvocationHandler() { | |||
@Override | |||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |||
// If the method is a method from Object then defer to normal invocation. | |||
if (method.getDeclaringClass() == Object.class) { | |||
return method.invoke(this, args); | |||
} | |||
ServiceMethod serviceMethod = loadServiceMethod(method); | |||
RemoteCall remoteCall = new RemoteCall(mTransferService, serviceMethod, args, mDispatcher); | |||
return serviceMethod.getCallAdapter().adapt(remoteCall); | |||
} | |||
}); | |||
} | |||
/** | |||
* Connect to the remote service. | |||
*/ | |||
public void bind() { | |||
Intent intent = new Intent(); | |||
if (!Utils.isStringBlank(mAction)) { | |||
intent.setAction(mAction); | |||
} else if (!Utils.isStringBlank(mClassName)) { | |||
intent.setClassName(mPackageName, mClassName); | |||
} | |||
// After android 5.0+, service Intent must be explicit. | |||
intent.setPackage(mPackageName); | |||
mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); | |||
} | |||
/** | |||
* Disconnect from the remote service. | |||
*/ | |||
public void unbind() { | |||
mContext.unbindService(mServiceConnection); | |||
} | |||
/** | |||
* Register client interface implementation called by remote service. | |||
*/ | |||
public void registerObject(Object target) { | |||
mInvoker.registerObject(target); | |||
} | |||
/** | |||
* Unregister client interface implementation. | |||
*/ | |||
public void unRegisterObject(Object target) { | |||
mInvoker.unRegisterObject(target); | |||
} | |||
/** | |||
* Set callback to be invoked when linker is bind or unBind. | |||
*/ | |||
public void setBindCallback(BindCallback bindCallback) { | |||
mBindCallback = bindCallback; | |||
} | |||
/** | |||
* Return the remote service bind state. | |||
*/ | |||
public boolean isBind() { | |||
return mTransferService != null; | |||
} | |||
CallAdapter<?, ?> findCallAdapter(Type returnType, Annotation[] annotations) { | |||
checkNotNull(returnType, "returnType == null"); | |||
checkNotNull(annotations, "annotations == null"); | |||
for (int i = 0, count = mAdapterFactories.size(); i < count; i++) { | |||
CallAdapter<?, ?> adapter = mAdapterFactories.get(i).get(returnType, annotations); | |||
if (adapter != null) { | |||
return adapter; | |||
} | |||
} | |||
return DefaultCallAdapterFactory.INSTANCE.get(returnType, annotations); | |||
} | |||
private ServiceConnection createServiceConnection() { | |||
return new ServiceConnection() { | |||
@Override | |||
public void onServiceConnected(ComponentName name, IBinder service) { | |||
mTransferService = ITransfer.Stub.asInterface(service); | |||
fireOnBind(); | |||
try { | |||
mTransferService.register(mCallback); | |||
} catch (RemoteException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
@Override | |||
public void onServiceDisconnected(ComponentName name) { | |||
if (mTransferService == null) { | |||
Logger.e(TAG, "Error occur, TransferService was null when service disconnected."); | |||
fireOnUnBind(); | |||
return; | |||
} | |||
try { | |||
mTransferService.unRegister(mCallback); | |||
} catch (RemoteException e) { | |||
e.printStackTrace(); | |||
} | |||
mTransferService = null; | |||
fireOnUnBind(); | |||
} | |||
}; | |||
} | |||
private void fireOnBind() { | |||
if (mBindCallback != null) { | |||
mBindCallback.onBind(); | |||
} | |||
} | |||
private void fireOnUnBind() { | |||
if (mBindCallback != null) { | |||
mBindCallback.onUnBind(); | |||
} | |||
} | |||
private ICallback createCallback() { | |||
return new ICallback.Stub() { | |||
@Override | |||
public Response callback(Request request) throws RemoteException { | |||
Logger.d(TAG, "Receive callback in client:" + request.toString()); | |||
return mInvoker.invoke(request); | |||
} | |||
}; | |||
} | |||
private ServiceMethod loadServiceMethod(Method method) { | |||
ServiceMethod result = serviceMethodCache.get(method); | |||
if (result != null) { | |||
return result; | |||
} | |||
synchronized (serviceMethodCache) { | |||
result = serviceMethodCache.get(method); | |||
if (result == null) { | |||
result = new ServiceMethod.Builder(this, method).build(); | |||
serviceMethodCache.put(method, result); | |||
} | |||
} | |||
return result; | |||
} | |||
/** | |||
* Method to enable or disable internal logger | |||
*/ | |||
public static void enableLogger(boolean enable) { | |||
Logger.sEnable = enable; | |||
} | |||
/** | |||
* Builder to create a new {@link AndLinker} instance. | |||
*/ | |||
public static final class Builder { | |||
private Context mContext; | |||
private String mPackageName; | |||
private String mAction; | |||
private String mClassName; | |||
private List<CallAdapter.Factory> mAdapterFactories = new ArrayList<>(); | |||
public Builder(Context context) { | |||
mContext = context; | |||
} | |||
/** | |||
* Set the remote service package name. | |||
*/ | |||
public Builder packageName(String packageName) { | |||
mPackageName = packageName; | |||
return this; | |||
} | |||
/** | |||
* Set the action to bind the remote service. | |||
*/ | |||
public Builder action(String action) { | |||
mAction = action; | |||
return this; | |||
} | |||
/** | |||
* Set the class name of the remote service. | |||
*/ | |||
public Builder className(String className) { | |||
mClassName = className; | |||
return this; | |||
} | |||
/** | |||
* Add a call adapter factory for supporting service method return types other than {@link Call}. | |||
*/ | |||
public Builder addCallAdapterFactory(CallAdapter.Factory factory) { | |||
mAdapterFactories.add(checkNotNull(factory, "factory == null")); | |||
return this; | |||
} | |||
/** | |||
* Create the {@link AndLinker} instance using the configured values. | |||
*/ | |||
public AndLinker build() { | |||
if (Utils.isStringBlank(mPackageName)) { | |||
throw new IllegalStateException("Package name required."); | |||
} | |||
if (Utils.isStringBlank(mAction) && Utils.isStringBlank(mClassName)) { | |||
throw new IllegalStateException("You must set one of the action or className."); | |||
} | |||
return new AndLinker(mContext, mPackageName, mAction, mClassName, mAdapterFactories); | |||
} | |||
} | |||
/** | |||
* Interface definition for a callback to be invoked when linker is bind or unBind to the service. | |||
*/ | |||
public interface BindCallback { | |||
/** | |||
* Called when a connection to the remote service has been established, now you can execute the remote call. | |||
*/ | |||
void onBind(); | |||
/** | |||
* Called when a connection to the remote service has been lost, any remote call will not execute. | |||
*/ | |||
void onUnBind(); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.IBinder; | |||
/** | |||
* AndLinker {@link IBinder} object to return in {@link android.app.Service#onBind(android.content.Intent)} method. | |||
*/ | |||
public interface AndLinkerBinder extends IBinder { | |||
/** | |||
* Register service interface implementation. | |||
*/ | |||
void registerObject(Object target); | |||
/** | |||
* Unregister service interface implementation. | |||
*/ | |||
void unRegisterObject(Object target); | |||
/** | |||
* {@link AndLinkerBinder} factory class. | |||
*/ | |||
final class Factory { | |||
private Factory() { | |||
} | |||
/** | |||
* Factory method to create the {@link AndLinkerBinder} impl instance. | |||
*/ | |||
public static AndLinkerBinder newBinder() { | |||
// Return inner package access LinkerBinder, prevent exposed. | |||
return new LinkerBinderImpl(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,356 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
import android.os.Parcelable; | |||
import java.lang.reflect.Array; | |||
/** | |||
* Created by codezjx on 2017/11/28.<br/> | |||
*/ | |||
interface ArrayType<T> extends OutType<T> { | |||
T newInstance(int length); | |||
final class ByteArrayType implements ArrayType<byte[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, byte[] val) { | |||
dest.writeByteArray(val); | |||
} | |||
@Override | |||
public byte[] createFromParcel(Parcel in) { | |||
return in.createByteArray(); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, byte[] val) { | |||
in.readByteArray(val); | |||
} | |||
@Override | |||
public byte[] newInstance(int length) { | |||
return new byte[length]; | |||
} | |||
} | |||
final class ShortArrayType implements ArrayType<short[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, short[] val) { | |||
if (val != null) { | |||
int N = val.length; | |||
dest.writeInt(N); | |||
for (int i = 0; i < N; i++) { | |||
dest.writeInt(val[i]); | |||
} | |||
} else { | |||
dest.writeInt(-1); | |||
} | |||
} | |||
@Override | |||
public short[] createFromParcel(Parcel in) { | |||
int N = in.readInt(); | |||
if (N >= 0 && N <= (in.dataAvail() >> 2)) { | |||
short[] val = new short[N]; | |||
for (int i = 0; i < N; i++) { | |||
val[i] = (short) in.readInt(); | |||
} | |||
return val; | |||
} else { | |||
return null; | |||
} | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, short[] val) { | |||
int N = in.readInt(); | |||
if (N == val.length) { | |||
for (int i = 0; i < N; i++) { | |||
val[i] = (short) in.readInt(); | |||
} | |||
} else { | |||
throw new RuntimeException("bad array lengths"); | |||
} | |||
} | |||
@Override | |||
public short[] newInstance(int length) { | |||
return new short[length]; | |||
} | |||
} | |||
final class IntArrayType implements ArrayType<int[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, int[] val) { | |||
dest.writeIntArray(val); | |||
} | |||
@Override | |||
public int[] createFromParcel(Parcel in) { | |||
return in.createIntArray(); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, int[] val) { | |||
in.readIntArray(val); | |||
} | |||
@Override | |||
public int[] newInstance(int length) { | |||
return new int[length]; | |||
} | |||
} | |||
final class LongArrayType implements ArrayType<long[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, long[] val) { | |||
dest.writeLongArray(val); | |||
} | |||
@Override | |||
public long[] createFromParcel(Parcel in) { | |||
return in.createLongArray(); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, long[] val) { | |||
in.readLongArray(val); | |||
} | |||
@Override | |||
public long[] newInstance(int length) { | |||
return new long[length]; | |||
} | |||
} | |||
final class FloatArrayType implements ArrayType<float[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, float[] val) { | |||
dest.writeFloatArray(val); | |||
} | |||
@Override | |||
public float[] createFromParcel(Parcel in) { | |||
return in.createFloatArray(); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, float[] val) { | |||
in.readFloatArray(val); | |||
} | |||
@Override | |||
public float[] newInstance(int length) { | |||
return new float[length]; | |||
} | |||
} | |||
final class DoubleArrayType implements ArrayType<double[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, double[] val) { | |||
dest.writeDoubleArray(val); | |||
} | |||
@Override | |||
public double[] createFromParcel(Parcel in) { | |||
return in.createDoubleArray(); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, double[] val) { | |||
in.readDoubleArray(val); | |||
} | |||
@Override | |||
public double[] newInstance(int length) { | |||
return new double[length]; | |||
} | |||
} | |||
final class BooleanArrayType implements ArrayType<boolean[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, boolean[] val) { | |||
dest.writeBooleanArray(val); | |||
} | |||
@Override | |||
public boolean[] createFromParcel(Parcel in) { | |||
return in.createBooleanArray(); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, boolean[] val) { | |||
in.readBooleanArray(val); | |||
} | |||
@Override | |||
public boolean[] newInstance(int length) { | |||
return new boolean[length]; | |||
} | |||
} | |||
final class CharArrayType implements ArrayType<char[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, char[] val) { | |||
dest.writeCharArray(val); | |||
} | |||
@Override | |||
public char[] createFromParcel(Parcel in) { | |||
return in.createCharArray(); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, char[] val) { | |||
in.readCharArray(val); | |||
} | |||
@Override | |||
public char[] newInstance(int length) { | |||
return new char[length]; | |||
} | |||
} | |||
final class StringArrayType implements ArrayType<String[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, String[] val) { | |||
dest.writeStringArray(val); | |||
} | |||
@Override | |||
public String[] createFromParcel(Parcel in) { | |||
return in.createStringArray(); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, String[] val) { | |||
in.readStringArray(val); | |||
} | |||
@Override | |||
public String[] newInstance(int length) { | |||
return new String[length]; | |||
} | |||
} | |||
final class CharSequenceArrayType implements ArrayType<CharSequence[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, CharSequence[] val) { | |||
writeCharSequenceArray(dest, val); | |||
} | |||
@Override | |||
public CharSequence[] createFromParcel(Parcel in) { | |||
return readCharSequenceArray(in); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, CharSequence[] val) { | |||
int N = in.readInt(); | |||
if (N == val.length) { | |||
for (int i = 0; i < N; i++) { | |||
val[i] = Type.CharSequenceType.readCharSequence(in); | |||
} | |||
} else { | |||
throw new RuntimeException("bad array lengths"); | |||
} | |||
} | |||
@Override | |||
public CharSequence[] newInstance(int length) { | |||
return new CharSequence[length]; | |||
} | |||
private void writeCharSequenceArray(Parcel dest, CharSequence[] val) { | |||
if (val != null) { | |||
int N = val.length; | |||
dest.writeInt(N); | |||
for (int i = 0; i < N; i++) { | |||
Type.CharSequenceType.writeCharSequence(dest, val[i]); | |||
} | |||
} else { | |||
dest.writeInt(-1); | |||
} | |||
} | |||
private CharSequence[] readCharSequenceArray(Parcel in) { | |||
CharSequence[] array = null; | |||
int length = in.readInt(); | |||
if (length >= 0) { | |||
array = new CharSequence[length]; | |||
for (int i = 0 ; i < length ; i++) { | |||
array[i] = Type.CharSequenceType.readCharSequence(in); | |||
} | |||
} | |||
return array; | |||
} | |||
} | |||
final class ParcelableArrayType implements ArrayType<Parcelable[]> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Parcelable[] val) { | |||
dest.writeString(val.getClass().getComponentType().getName()); | |||
dest.writeParcelableArray(val, flags); | |||
} | |||
@Override | |||
public Parcelable[] createFromParcel(Parcel in) { | |||
String componentType = in.readString(); | |||
Object obj = null; | |||
try { | |||
Class cls = Class.forName(componentType); | |||
obj = createTypedArray(in, cls); | |||
} catch (ClassNotFoundException e) { | |||
e.printStackTrace(); | |||
} | |||
return (Parcelable[]) obj; | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, Parcelable[] val) { | |||
// Just read and do nothing, because we don't need component type here | |||
in.readString(); | |||
int N = in.readInt(); | |||
if (N == val.length) { | |||
for (int i = 0; i < N; i++) { | |||
val[i] = in.readParcelable(getClass().getClassLoader()); | |||
} | |||
} else { | |||
throw new RuntimeException("bad array lengths"); | |||
} | |||
} | |||
@Override | |||
public Parcelable[] newInstance(int length) { | |||
return new Parcelable[length]; | |||
} | |||
private <T> T[] createTypedArray(Parcel in, Class<T> cls) { | |||
int N = in.readInt(); | |||
if (N < 0) { | |||
return null; | |||
} | |||
T[] arr = (T[]) Array.newInstance(cls, N); | |||
for (int i = 0; i < N; i++) { | |||
arr[i] = in.readParcelable(cls.getClassLoader()); | |||
} | |||
return arr; | |||
} | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
package com.codezjx.andlinker; | |||
/** | |||
* Created by codezjx on 2017/11/19.<br/> | |||
*/ | |||
interface BaseTypeWrapper extends SuperParcelable { | |||
int TYPE_EMPTY = 0; | |||
// Primitives | |||
int TYPE_BYTE = 1; | |||
int TYPE_SHORT = 2; | |||
int TYPE_INT = 3; | |||
int TYPE_LONG = 4; | |||
int TYPE_FLOAT = 5; | |||
int TYPE_DOUBLE = 6; | |||
int TYPE_BOOLEAN = 7; | |||
int TYPE_CHAR = 8; | |||
// Primitive Arrays | |||
int TYPE_BYTEARRAY = 9; | |||
int TYPE_SHORTARRAY = 10; | |||
int TYPE_INTARRAY = 11; | |||
int TYPE_LONGARRAY = 12; | |||
int TYPE_FLOATARRAY = 13; | |||
int TYPE_DOUBLEARRAY = 14; | |||
int TYPE_BOOLEANARRAY = 15; | |||
int TYPE_CHARARRAY = 16; | |||
// Other | |||
int TYPE_STRING = 17; | |||
int TYPE_STRINGARRAY = 18; | |||
int TYPE_CHARSEQUENCE = 19; | |||
int TYPE_CHARSEQUENCEARRAY = 20; | |||
int TYPE_PARCELABLE = 21; | |||
int TYPE_PARCELABLEARRAY = 22; | |||
int TYPE_LIST = 23; | |||
int TYPE_MAP = 24; | |||
int TYPE_CALLBACK = 25; | |||
int getType(); | |||
Object getParam(); | |||
} |
@@ -0,0 +1,34 @@ | |||
package com.codezjx.andlinker; | |||
/** | |||
* An invocation of a remote method that sends a request to host and returns a response. | |||
*/ | |||
public interface Call<T> { | |||
/** | |||
* Synchronously send the request and return its response. | |||
*/ | |||
T execute(); | |||
/** | |||
* Asynchronously send the request and notify when response return. | |||
* @param callback The callback to notify. | |||
*/ | |||
void enqueue(Callback<T> callback); | |||
/** | |||
* Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain #enqueue(Callback) enqueued}. | |||
*/ | |||
boolean isExecuted(); | |||
/** | |||
* Cancel this call. If the call has not yet been executed it never will be. | |||
*/ | |||
void cancel(); | |||
/** | |||
* True if {@link #cancel()} was called. | |||
*/ | |||
boolean isCanceled(); | |||
} |
@@ -0,0 +1,36 @@ | |||
package com.codezjx.andlinker; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.Type; | |||
/** | |||
* Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are | |||
* created by {@linkplain Factory factory} which is {@linkplain AndLinker.Builder#addCallAdapterFactory(Factory) installed} | |||
* into the {@link AndLinker} instance. | |||
*/ | |||
public interface CallAdapter<R, T> { | |||
/** | |||
* Returns an instance of {@code T} which delegates to {@code call}. | |||
*/ | |||
T adapt(Call<R> call); | |||
/** | |||
* Creates {@link CallAdapter} instances based on the return type of {@linkplain | |||
* AndLinker#create(Class) the service interface} methods. | |||
*/ | |||
abstract class Factory { | |||
/** | |||
* Returns a call adapter for interface methods that return {@code returnType}, or null if it | |||
* cannot be handled by this factory. | |||
*/ | |||
public abstract CallAdapter<?, ?> get(Type returnType, Annotation[] annotations); | |||
protected static Class<?> getRawType(Type type) { | |||
return Utils.getRawType(type); | |||
} | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.codezjx.andlinker; | |||
/** | |||
* Interface definition for a callback to be invoked when execute {@linkplain Call#enqueue(Callback) asynchronously call}. | |||
* By default callbacks are executed on the application's main (UI) thread. | |||
*/ | |||
public interface Callback<T> { | |||
/** | |||
* Invoked for a received response. | |||
*/ | |||
void onResponse(Call<T> call, T response); | |||
/** | |||
* Invoked when a remote exception occurred. | |||
*/ | |||
void onFailure(Call<T> call, Throwable t); | |||
} |
@@ -0,0 +1,60 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
/** | |||
* Created by codezjx on 2017/11/18.<br/> | |||
*/ | |||
final class CallbackTypeWrapper implements BaseTypeWrapper { | |||
private String mClassName; | |||
CallbackTypeWrapper(String className) { | |||
mClassName = className; | |||
} | |||
String getClassName() { | |||
return mClassName; | |||
} | |||
@Override | |||
public int getType() { | |||
return TYPE_CALLBACK; | |||
} | |||
@Override | |||
public Object getParam() { | |||
return null; | |||
} | |||
@Override | |||
public int describeContents() { | |||
return 0; | |||
} | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags) { | |||
dest.writeString(mClassName); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in) { | |||
mClassName = in.readString(); | |||
} | |||
private CallbackTypeWrapper(Parcel in) { | |||
readFromParcel(in); | |||
} | |||
public static final Creator<CallbackTypeWrapper> CREATOR = new Creator<CallbackTypeWrapper>() { | |||
@Override | |||
public CallbackTypeWrapper createFromParcel(Parcel source) { | |||
return new CallbackTypeWrapper(source); | |||
} | |||
@Override | |||
public CallbackTypeWrapper[] newArray(int size) { | |||
return new CallbackTypeWrapper[size]; | |||
} | |||
}; | |||
} |
@@ -0,0 +1,49 @@ | |||
package com.codezjx.andlinker; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.SynchronousQueue; | |||
import java.util.concurrent.ThreadFactory; | |||
import java.util.concurrent.ThreadPoolExecutor; | |||
import java.util.concurrent.TimeUnit; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
/** | |||
* Created by codezjx on 2018/1/10.<br/> | |||
*/ | |||
final class Dispatcher { | |||
private static final String THREAD_NAME = "Dispatcher Thread #"; | |||
private static final int KEEP_ALIVE_TIME_SECONDS = 60; | |||
private ExecutorService mExecutorService; | |||
Dispatcher() { | |||
} | |||
Dispatcher(ExecutorService executorService) { | |||
mExecutorService = executorService; | |||
} | |||
synchronized void enqueue(Runnable task) { | |||
if (mExecutorService == null) { | |||
mExecutorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, | |||
KEEP_ALIVE_TIME_SECONDS, TimeUnit.SECONDS, | |||
new SynchronousQueue<Runnable>(), createFactory()); | |||
} | |||
mExecutorService.execute(task); | |||
} | |||
private ThreadFactory createFactory() { | |||
return new ThreadFactory() { | |||
private final AtomicInteger mCount = new AtomicInteger(1); | |||
@Override | |||
public Thread newThread(Runnable runnable) { | |||
Thread thread = new Thread(runnable, THREAD_NAME + mCount.getAndIncrement()); | |||
thread.setDaemon(false); | |||
return thread; | |||
} | |||
}; | |||
} | |||
} |
@@ -0,0 +1,114 @@ | |||
package com.codezjx.andlinker; | |||
interface ICallback extends android.os.IInterface { | |||
/** | |||
* Local-side IPC implementation stub class. | |||
*/ | |||
abstract class Stub extends android.os.Binder implements ICallback { | |||
private static final String DESCRIPTOR = "com.codezjx.alinker.ICallback"; | |||
/** | |||
* Construct the stub at attach it to the interface. | |||
*/ | |||
Stub() { | |||
this.attachInterface(this, DESCRIPTOR); | |||
} | |||
/** | |||
* Cast an IBinder object into an com.codezjx.alinker.ICallback interface, | |||
* generating a proxy if needed. | |||
*/ | |||
static ICallback asInterface(android.os.IBinder obj) { | |||
if ((obj == null)) { | |||
return null; | |||
} | |||
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); | |||
if (((iin != null) && (iin instanceof ICallback))) { | |||
return ((ICallback) iin); | |||
} | |||
return new Proxy(obj); | |||
} | |||
@Override | |||
public android.os.IBinder asBinder() { | |||
return this; | |||
} | |||
@Override | |||
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { | |||
switch (code) { | |||
case INTERFACE_TRANSACTION: { | |||
reply.writeString(DESCRIPTOR); | |||
return true; | |||
} | |||
case TRANSACTION_callback: { | |||
data.enforceInterface(DESCRIPTOR); | |||
Request _arg0; | |||
if ((0 != data.readInt())) { | |||
_arg0 = Request.CREATOR.createFromParcel(data); | |||
} else { | |||
_arg0 = null; | |||
} | |||
Response _result = this.callback(_arg0); | |||
reply.writeNoException(); | |||
if ((_result != null)) { | |||
reply.writeInt(1); | |||
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); | |||
} else { | |||
reply.writeInt(0); | |||
} | |||
return true; | |||
} | |||
} | |||
return super.onTransact(code, data, reply, flags); | |||
} | |||
private static class Proxy implements ICallback { | |||
private android.os.IBinder mRemote; | |||
Proxy(android.os.IBinder remote) { | |||
mRemote = remote; | |||
} | |||
@Override | |||
public android.os.IBinder asBinder() { | |||
return mRemote; | |||
} | |||
public String getInterfaceDescriptor() { | |||
return DESCRIPTOR; | |||
} | |||
@Override | |||
public Response callback(Request request) throws android.os.RemoteException { | |||
android.os.Parcel _data = android.os.Parcel.obtain(); | |||
android.os.Parcel _reply = android.os.Parcel.obtain(); | |||
Response _result; | |||
try { | |||
_data.writeInterfaceToken(DESCRIPTOR); | |||
if ((request != null)) { | |||
_data.writeInt(1); | |||
request.writeToParcel(_data, 0); | |||
} else { | |||
_data.writeInt(0); | |||
} | |||
mRemote.transact(Stub.TRANSACTION_callback, _data, _reply, 0); | |||
_reply.readException(); | |||
if ((0 != _reply.readInt())) { | |||
_result = Response.CREATOR.createFromParcel(_reply); | |||
} else { | |||
_result = null; | |||
} | |||
} finally { | |||
_reply.recycle(); | |||
_data.recycle(); | |||
} | |||
return _result; | |||
} | |||
} | |||
static final int TRANSACTION_callback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); | |||
} | |||
Response callback(Request request) throws android.os.RemoteException; | |||
} |
@@ -0,0 +1,184 @@ | |||
package com.codezjx.andlinker; | |||
interface ITransfer extends android.os.IInterface { | |||
/** | |||
* Local-side IPC implementation stub class. | |||
*/ | |||
abstract class Stub extends android.os.Binder implements ITransfer { | |||
private static final String DESCRIPTOR = "com.codezjx.alinker.ITransfer"; | |||
/** | |||
* Construct the stub at attach it to the interface. | |||
*/ | |||
Stub() { | |||
this.attachInterface(this, DESCRIPTOR); | |||
} | |||
/** | |||
* Cast an IBinder object into an com.codezjx.alinker.ITransfer interface, | |||
* generating a proxy if needed. | |||
*/ | |||
static ITransfer asInterface(android.os.IBinder obj) { | |||
if ((obj == null)) { | |||
return null; | |||
} | |||
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); | |||
if (((iin != null) && (iin instanceof ITransfer))) { | |||
return ((ITransfer) iin); | |||
} | |||
return new Proxy(obj); | |||
} | |||
@Override | |||
public android.os.IBinder asBinder() { | |||
return this; | |||
} | |||
@Override | |||
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { | |||
switch (code) { | |||
case INTERFACE_TRANSACTION: { | |||
reply.writeString(DESCRIPTOR); | |||
return true; | |||
} | |||
case TRANSACTION_execute: { | |||
data.enforceInterface(DESCRIPTOR); | |||
Request _arg0; | |||
if ((0 != data.readInt())) { | |||
_arg0 = Request.CREATOR.createFromParcel(data); | |||
} else { | |||
_arg0 = null; | |||
} | |||
Response _result = this.execute(_arg0); | |||
if ((flags & android.os.IBinder.FLAG_ONEWAY) != 0) { | |||
// One-way mode just execute and return directly. | |||
return true; | |||
} | |||
reply.writeNoException(); | |||
if ((_result != null)) { | |||
reply.writeInt(1); | |||
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); | |||
} else { | |||
reply.writeInt(0); | |||
} | |||
if ((_arg0 != null)) { | |||
reply.writeInt(1); | |||
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); | |||
} else { | |||
reply.writeInt(0); | |||
} | |||
return true; | |||
} | |||
case TRANSACTION_register: { | |||
data.enforceInterface(DESCRIPTOR); | |||
ICallback _arg0; | |||
_arg0 = ICallback.Stub.asInterface(data.readStrongBinder()); | |||
this.register(_arg0); | |||
reply.writeNoException(); | |||
return true; | |||
} | |||
case TRANSACTION_unRegister: { | |||
data.enforceInterface(DESCRIPTOR); | |||
ICallback _arg0; | |||
_arg0 = ICallback.Stub.asInterface(data.readStrongBinder()); | |||
this.unRegister(_arg0); | |||
reply.writeNoException(); | |||
return true; | |||
} | |||
} | |||
return super.onTransact(code, data, reply, flags); | |||
} | |||
private static class Proxy implements ITransfer { | |||
private android.os.IBinder mRemote; | |||
Proxy(android.os.IBinder remote) { | |||
mRemote = remote; | |||
} | |||
@Override | |||
public android.os.IBinder asBinder() { | |||
return mRemote; | |||
} | |||
public String getInterfaceDescriptor() { | |||
return DESCRIPTOR; | |||
} | |||
@Override | |||
public Response execute(Request request) throws android.os.RemoteException { | |||
android.os.Parcel _data = android.os.Parcel.obtain(); | |||
android.os.Parcel _reply = android.os.Parcel.obtain(); | |||
Response _result; | |||
try { | |||
_data.writeInterfaceToken(DESCRIPTOR); | |||
if ((request != null)) { | |||
_data.writeInt(1); | |||
request.writeToParcel(_data, 0); | |||
} else { | |||
_data.writeInt(0); | |||
} | |||
// One-way mode just transact and return directly. | |||
if (request != null && request.isOneWay()) { | |||
mRemote.transact(Stub.TRANSACTION_execute, _data, null, android.os.IBinder.FLAG_ONEWAY); | |||
return null; | |||
} | |||
mRemote.transact(Stub.TRANSACTION_execute, _data, _reply, 0); | |||
_reply.readException(); | |||
if ((0 != _reply.readInt())) { | |||
_result = Response.CREATOR.createFromParcel(_reply); | |||
} else { | |||
_result = null; | |||
} | |||
if ((0 != _reply.readInt())) { | |||
request.readFromParcel(_reply); | |||
} | |||
} finally { | |||
_reply.recycle(); | |||
_data.recycle(); | |||
} | |||
return _result; | |||
} | |||
@Override | |||
public void register(ICallback callback) throws android.os.RemoteException { | |||
android.os.Parcel _data = android.os.Parcel.obtain(); | |||
android.os.Parcel _reply = android.os.Parcel.obtain(); | |||
try { | |||
_data.writeInterfaceToken(DESCRIPTOR); | |||
_data.writeStrongBinder((((callback != null)) ? (callback.asBinder()) : (null))); | |||
mRemote.transact(Stub.TRANSACTION_register, _data, _reply, 0); | |||
_reply.readException(); | |||
} finally { | |||
_reply.recycle(); | |||
_data.recycle(); | |||
} | |||
} | |||
@Override | |||
public void unRegister(ICallback callback) throws android.os.RemoteException { | |||
android.os.Parcel _data = android.os.Parcel.obtain(); | |||
android.os.Parcel _reply = android.os.Parcel.obtain(); | |||
try { | |||
_data.writeInterfaceToken(DESCRIPTOR); | |||
_data.writeStrongBinder((((callback != null)) ? (callback.asBinder()) : (null))); | |||
mRemote.transact(Stub.TRANSACTION_unRegister, _data, _reply, 0); | |||
_reply.readException(); | |||
} finally { | |||
_reply.recycle(); | |||
_data.recycle(); | |||
} | |||
} | |||
} | |||
static final int TRANSACTION_execute = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); | |||
static final int TRANSACTION_register = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); | |||
static final int TRANSACTION_unRegister = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); | |||
} | |||
Response execute(Request request) throws android.os.RemoteException; | |||
void register(ICallback callback) throws android.os.RemoteException; | |||
void unRegister(ICallback callback) throws android.os.RemoteException; | |||
} |
@@ -0,0 +1,65 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
/** | |||
* Created by codezjx on 2017/11/30.<br/> | |||
*/ | |||
final class InOutTypeWrapper implements BaseTypeWrapper { | |||
private int mType; | |||
private Object mParam; | |||
InOutTypeWrapper(Object param, Class<?> mParamType) { | |||
mType = Utils.getTypeByClass(mParamType); | |||
mParam = param; | |||
} | |||
@Override | |||
public int getType() { | |||
return mType; | |||
} | |||
@Override | |||
public Object getParam() { | |||
return mParam; | |||
} | |||
@Override | |||
public int describeContents() { | |||
return 0; | |||
} | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags) { | |||
dest.writeInt(mType); | |||
Type type = TypeFactory.getType(mType); | |||
type.writeToParcel(dest, flags, mParam); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in) { | |||
mType = in.readInt(); | |||
OutType type = (OutType) TypeFactory.getType(mType); | |||
type.readFromParcel(in, mParam); | |||
} | |||
private InOutTypeWrapper(Parcel in) { | |||
mType = in.readInt(); | |||
Type type = TypeFactory.getType(mType); | |||
mParam = type.createFromParcel(in); | |||
} | |||
public static final Creator<InOutTypeWrapper> CREATOR = new Creator<InOutTypeWrapper>() { | |||
@Override | |||
public InOutTypeWrapper createFromParcel(Parcel source) { | |||
return new InOutTypeWrapper(source); | |||
} | |||
@Override | |||
public InOutTypeWrapper[] newArray(int size) { | |||
return new InOutTypeWrapper[size]; | |||
} | |||
}; | |||
} |
@@ -0,0 +1,67 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
/** | |||
* Created by codezjx on 2017/11/19.<br/> | |||
*/ | |||
final class InTypeWrapper implements BaseTypeWrapper { | |||
private int mType; | |||
private Object mParam; | |||
InTypeWrapper(Object param, Class<?> mParamType) { | |||
mType = Utils.getTypeByClass(mParamType); | |||
mParam = param; | |||
} | |||
@Override | |||
public int getType() { | |||
return mType; | |||
} | |||
@Override | |||
public Object getParam() { | |||
return mParam; | |||
} | |||
@Override | |||
public int describeContents() { | |||
return 0; | |||
} | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags) { | |||
// Return if write return value, because in type won't readFromParcel | |||
if (flags == PARCELABLE_WRITE_RETURN_VALUE) { | |||
return; | |||
} | |||
dest.writeInt(mType); | |||
Type type = TypeFactory.getType(mType); | |||
type.writeToParcel(dest, flags, mParam); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in) { | |||
// Nothing to do with in type | |||
} | |||
private InTypeWrapper(Parcel in) { | |||
mType = in.readInt(); | |||
Type type = TypeFactory.getType(mType); | |||
mParam = type.createFromParcel(in); | |||
} | |||
public static final Creator<InTypeWrapper> CREATOR = new Creator<InTypeWrapper>() { | |||
@Override | |||
public InTypeWrapper createFromParcel(Parcel source) { | |||
return new InTypeWrapper(source); | |||
} | |||
@Override | |||
public InTypeWrapper[] newArray(int size) { | |||
return new InTypeWrapper[size]; | |||
} | |||
}; | |||
} |
@@ -0,0 +1,197 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Binder; | |||
import android.os.RemoteCallbackList; | |||
import android.os.RemoteException; | |||
import com.codezjx.andlinker.annotation.RemoteInterface; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.InvocationHandler; | |||
import java.lang.reflect.Method; | |||
import java.lang.reflect.Proxy; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
/** | |||
* Created by codezjx on 2017/10/3.<br/> | |||
*/ | |||
final class Invoker { | |||
private static final String TAG = "Invoker"; | |||
private final ConcurrentHashMap<String, Class<?>> mCallbackClassTypes; | |||
private final ConcurrentHashMap<String, MethodExecutor> mMethodExecutors; | |||
private final RemoteCallbackList<ICallback> mCallbackList; | |||
Invoker() { | |||
mCallbackClassTypes = new ConcurrentHashMap<String, Class<?>>(); | |||
mMethodExecutors = new ConcurrentHashMap<String, MethodExecutor>(); | |||
mCallbackList = new RemoteCallbackList<ICallback>(); | |||
} | |||
private void handleCallbackClass(Class<?> clazz, boolean isRegister) { | |||
if (!clazz.isAnnotationPresent(RemoteInterface.class)) { | |||
throw new IllegalArgumentException("Callback interface doesn't has @RemoteInterface annotation."); | |||
} | |||
String className = clazz.getSimpleName(); | |||
if (isRegister) { | |||
mCallbackClassTypes.putIfAbsent(className, clazz); | |||
} else { | |||
mCallbackClassTypes.remove(className); | |||
} | |||
} | |||
private void handleObject(Object target, boolean isRegister) { | |||
if (target == null) { | |||
throw new NullPointerException("Object to (un)register must not be null."); | |||
} | |||
Class<?>[] interfaces = target.getClass().getInterfaces(); | |||
if (interfaces.length != 1) { | |||
throw new IllegalArgumentException("Remote object must extend just one interface."); | |||
} | |||
Class<?> clazz = interfaces[0]; | |||
if (!clazz.isAnnotationPresent(RemoteInterface.class)) { | |||
throw new IllegalArgumentException("Interface doesn't has @RemoteInterface annotation."); | |||
} | |||
// Cache all annotation method | |||
String clsName = clazz.getSimpleName(); | |||
Method[] methods = clazz.getDeclaredMethods(); | |||
for (Method method : methods) { | |||
// The compiler sometimes creates synthetic bridge methods as part of the | |||
// type erasure process. As of JDK8 these methods now include the same | |||
// annotations as the original declarations. They should be ignored for | |||
// subscribe/produce. | |||
if (method.isBridge()) { | |||
continue; | |||
} | |||
String methodName = method.getName(); | |||
String key = createMethodExecutorKey(clsName, methodName); | |||
if (isRegister) { | |||
MethodExecutor executor = new MethodExecutor(target, method); | |||
MethodExecutor preExecutor = mMethodExecutors.putIfAbsent(key, executor); | |||
if (preExecutor != null) { | |||
throw new IllegalStateException("Key conflict with class:" + clsName + " method:" + methodName | |||
+ ". Please try another class/method name."); | |||
} | |||
} else { | |||
mMethodExecutors.remove(key); | |||
} | |||
// Cache callback class if exist | |||
Class<?>[] paramCls = method.getParameterTypes(); | |||
Annotation[][] paramAnnotations = method.getParameterAnnotations(); | |||
for (int i = 0; i < paramCls.length; i++) { | |||
Class<?> cls = paramCls[i]; | |||
Annotation[] annotations = paramAnnotations[i]; | |||
if (!containCallbackAnnotation(annotations)) { | |||
continue; | |||
} | |||
handleCallbackClass(cls, isRegister); | |||
} | |||
} | |||
} | |||
private boolean containCallbackAnnotation(Annotation[] annotations) { | |||
if (annotations == null) { | |||
return false; | |||
} | |||
for (Annotation annotation : annotations) { | |||
if (annotation instanceof com.codezjx.andlinker.annotation.Callback) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
void registerObject(Object target) { | |||
handleObject(target, true); | |||
} | |||
void unRegisterObject(Object target) { | |||
handleObject(target, false); | |||
} | |||
Response invoke(Request request) { | |||
BaseTypeWrapper[] wrappers = request.getArgsWrapper(); | |||
Object[] args = new Object[wrappers.length]; | |||
for (int i = 0; i < wrappers.length; i++) { | |||
// Assign the origin args parameter | |||
args[i] = wrappers[i].getParam(); | |||
if (wrappers[i].getType() == BaseTypeWrapper.TYPE_CALLBACK) { | |||
int pid = Binder.getCallingPid(); | |||
String clazzName = ((CallbackTypeWrapper) wrappers[i]).getClassName(); | |||
Class<?> clazz = getCallbackClass(clazzName); | |||
if (clazz == null) { | |||
throw new IllegalStateException("Can't find callback class: " + clazzName); | |||
} | |||
args[i] = getCallbackProxy(clazz, pid); | |||
} | |||
} | |||
MethodExecutor executor = getMethodExecutor(request); | |||
if (executor == null) { | |||
String errMsg = String.format("The method '%s' you call was not exist!", request.getMethodName()); | |||
return new Response(Response.STATUS_CODE_NOT_FOUND, errMsg, null); | |||
} | |||
return executor.execute(args); | |||
} | |||
RemoteCallbackList<ICallback> getCallbackList() { | |||
return mCallbackList; | |||
} | |||
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety. | |||
private <T> T getCallbackProxy(final Class<T> service, final int pid) { | |||
Utils.validateServiceInterface(service); | |||
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, | |||
new InvocationHandler() { | |||
@Override | |||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |||
Object result = null; | |||
final int len = mCallbackList.beginBroadcast(); | |||
for (int i = 0; i < len; i++) { | |||
int cookiePid = (int) mCallbackList.getBroadcastCookie(i); | |||
if (cookiePid == pid) { | |||
try { | |||
Request request = createCallbackRequest(service.getSimpleName(), method.getName(), args); | |||
Response response = mCallbackList.getBroadcastItem(i).callback(request); | |||
result = response.getResult(); | |||
if (response.getStatusCode() != Response.STATUS_CODE_SUCCESS) { | |||
Logger.e(TAG, "Execute remote callback fail: " + response.toString()); | |||
} | |||
} catch (RemoteException e) { | |||
Logger.e(TAG, "Error when execute callback!", e); | |||
} | |||
break; | |||
} | |||
} | |||
mCallbackList.finishBroadcast(); | |||
return result; | |||
} | |||
}); | |||
} | |||
private Request createCallbackRequest(String targetClass, String methodName, Object[] args) { | |||
BaseTypeWrapper[] wrappers = new BaseTypeWrapper[args.length]; | |||
for (int i = 0; i < args.length; i++) { | |||
wrappers[i] = new InTypeWrapper(args[i], args[i].getClass()); | |||
} | |||
return new Request(targetClass, methodName, wrappers); | |||
} | |||
private Class<?> getCallbackClass(String className) { | |||
return mCallbackClassTypes.get(className); | |||
} | |||
private String createMethodExecutorKey(String clsName, String methodName) { | |||
StringBuilder sb = new StringBuilder(); | |||
sb.append(clsName) | |||
.append('-') | |||
.append(methodName); | |||
return sb.toString(); | |||
} | |||
private MethodExecutor getMethodExecutor(Request request) { | |||
String key = createMethodExecutorKey(request.getTargetClass(), request.getMethodName()); | |||
return mMethodExecutors.get(key); | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Binder; | |||
import android.os.RemoteCallbackList; | |||
import android.os.RemoteException; | |||
/** | |||
* Created by codezjx on 2017/9/13.<br/> | |||
*/ | |||
final class LinkerBinderImpl extends ITransfer.Stub implements AndLinkerBinder { | |||
private static final String TAG = "LinkerBinder"; | |||
private RemoteCallbackList<ICallback> mCallbackList; | |||
private Invoker mInvoker; | |||
LinkerBinderImpl() { | |||
mInvoker = new Invoker(); | |||
mCallbackList = mInvoker.getCallbackList(); | |||
} | |||
@Override | |||
public void registerObject(Object target) { | |||
mInvoker.registerObject(target); | |||
} | |||
@Override | |||
public void unRegisterObject(Object target) { | |||
mInvoker.unRegisterObject(target); | |||
} | |||
@Override | |||
public Response execute(Request request) throws RemoteException { | |||
for (BaseTypeWrapper wrapper : request.getArgsWrapper()) { | |||
Logger.d(TAG, "Receive param, value:" + wrapper.getParam() | |||
+ " type:" + (wrapper.getParam() != null ? wrapper.getParam().getClass() : "null")); | |||
} | |||
Logger.d(TAG, "Receive request:" + request.getMethodName()); | |||
return mInvoker.invoke(request); | |||
} | |||
@Override | |||
public void register(ICallback callback) throws RemoteException { | |||
int pid = Binder.getCallingPid(); | |||
Logger.d(TAG, "register callback:" + callback + " pid:" + pid); | |||
if (callback != null) { | |||
mCallbackList.register(callback, pid); | |||
} | |||
} | |||
@Override | |||
public void unRegister(ICallback callback) throws RemoteException { | |||
int pid = Binder.getCallingPid(); | |||
Logger.d(TAG, "unRegister callback:" + callback + " pid:" + pid); | |||
if (callback != null) { | |||
mCallbackList.unregister(callback); | |||
} | |||
} | |||
} |
@@ -0,0 +1,127 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Build; | |||
import android.util.Log; | |||
import java.io.PrintWriter; | |||
import java.io.StringWriter; | |||
import java.util.regex.Matcher; | |||
import java.util.regex.Pattern; | |||
/** | |||
* Created by codezjx on 2018/1/23.<br/> | |||
*/ | |||
final class Logger { | |||
private Logger() { | |||
// private constructor | |||
} | |||
private static final String DEFAULT_LOG_TAG = "Logger"; | |||
private static final int MAX_TAG_LENGTH = 23; | |||
// 3 method calls inside Logger | |||
private static final int CALL_STACK_INDEX = 3; | |||
private static final Pattern ANONYMOUS_CLASS = Pattern.compile("(\\$\\d+)+$"); | |||
static boolean sEnable = false; | |||
static void v(String msg) { | |||
log(Log.VERBOSE, null, msg, null); | |||
} | |||
static void v(String tag, String msg) { | |||
log(Log.VERBOSE, tag, msg, null); | |||
} | |||
static void d(String msg) { | |||
log(Log.DEBUG, null, msg, null); | |||
} | |||
static void d(String tag, String msg) { | |||
log(Log.DEBUG, tag, msg, null); | |||
} | |||
static void i(String msg) { | |||
log(Log.INFO, null, msg, null); | |||
} | |||
static void i(String tag, String msg) { | |||
log(Log.INFO, tag, msg, null); | |||
} | |||
static void w(String msg) { | |||
log(Log.WARN, null, msg, null); | |||
} | |||
static void w(String tag, String msg) { | |||
log(Log.WARN, tag, msg, null); | |||
} | |||
static void w(String msg, Throwable t) { | |||
log(Log.WARN, null, msg, t); | |||
} | |||
static void w(String tag, String msg, Throwable t) { | |||
log(Log.WARN, tag, msg, t); | |||
} | |||
static void e(String msg) { | |||
log(Log.ERROR, null, msg, null); | |||
} | |||
static void e(String tag, String msg) { | |||
log(Log.ERROR, tag, msg, null); | |||
} | |||
static void e(String msg, Throwable t) { | |||
log(Log.ERROR, null, msg, t); | |||
} | |||
static void e(String tag, String msg, Throwable t) { | |||
log(Log.ERROR, tag, msg, t); | |||
} | |||
private static void log(int priority, String tag, String msg, Throwable t) { | |||
if (!sEnable) { | |||
return; | |||
} | |||
String curTag = (tag != null && tag.trim().length() > 0) ? tag : getTag(); | |||
String curMsg = (t != null) ? (msg + '\n' + getStackTraceString(t)) : msg; | |||
Log.println(priority, curTag, curMsg); | |||
} | |||
private static String getStackTraceString(Throwable t) { | |||
if (t == null) { | |||
return ""; | |||
} | |||
// Don't replace this with Log.getStackTraceString() - it hides | |||
// UnknownHostException, which is not what we want. | |||
StringWriter sw = new StringWriter(256); | |||
PrintWriter pw = new PrintWriter(sw, false); | |||
t.printStackTrace(pw); | |||
pw.flush(); | |||
return sw.toString(); | |||
} | |||
private static String getTag() { | |||
StackTraceElement[] stackTrace = new Throwable().getStackTrace(); | |||
if (stackTrace.length <= CALL_STACK_INDEX) { | |||
Log.e(DEFAULT_LOG_TAG, "Synthetic stacktrace didn't have enough elements, use the default logger tag, are you using proguard?"); | |||
return DEFAULT_LOG_TAG; | |||
} | |||
return createStackElementTag(stackTrace[CALL_STACK_INDEX]); | |||
} | |||
private static String createStackElementTag(StackTraceElement element) { | |||
String tag = element.getClassName(); | |||
Matcher m = ANONYMOUS_CLASS.matcher(tag); | |||
if (m.find()) { | |||
tag = m.replaceAll(""); | |||
} | |||
tag = tag.substring(tag.lastIndexOf('.') + 1); | |||
// Tag length limit was removed in API 24. | |||
if (tag.length() <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { | |||
return tag; | |||
} | |||
return tag.substring(0, MAX_TAG_LENGTH); | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
package com.codezjx.andlinker; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
/** | |||
* Created by codezjx on 2017/10/22.<br/> | |||
*/ | |||
final class MethodExecutor { | |||
/** Target object to invoke. */ | |||
private final Object mTarget; | |||
/** Method to execute. */ | |||
private final Method mMethod; | |||
MethodExecutor(Object target, Method method) { | |||
if (target == null) { | |||
throw new NullPointerException("Target cannot be null."); | |||
} | |||
if (method == null) { | |||
throw new NullPointerException("Method cannot be null."); | |||
} | |||
mTarget = target; | |||
mMethod = method; | |||
method.setAccessible(true); | |||
} | |||
Response execute(Object[] args) { | |||
Object result = null; | |||
int statusCode = Response.STATUS_CODE_SUCCESS; | |||
String resultMsg = String.format("Call method '%s' successfully!", mMethod.getName()); | |||
Throwable throwable = null; | |||
try { | |||
result = mMethod.invoke(mTarget, args); | |||
} catch (IllegalAccessException e) { | |||
statusCode = Response.STATUS_CODE_ILLEGAL_ACCESS; | |||
throwable = e; | |||
} catch (InvocationTargetException e) { | |||
statusCode = Response.STATUS_CODE_INVOCATION_FAIL; | |||
throwable = e; | |||
} | |||
if (throwable != null) { | |||
resultMsg = "Exception occur when execute method:" + mMethod.getName() + '\n' + throwable.getMessage(); | |||
} | |||
return new Response(statusCode, resultMsg, result); | |||
} | |||
} |
@@ -0,0 +1,109 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
import android.os.Parcelable; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* Created by codezjx on 2017/11/30.<br/> | |||
*/ | |||
interface OutType<T> extends Type<T> { | |||
void readFromParcel(Parcel in, T val); | |||
final class ParcelableType implements OutType<Parcelable> { | |||
private static final String TAG = "ParcelableType"; | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Parcelable val) { | |||
if (flags == Parcelable.PARCELABLE_WRITE_RETURN_VALUE) { | |||
val.writeToParcel(dest, flags); | |||
} else { | |||
dest.writeParcelable(val, flags); | |||
} | |||
} | |||
@Override | |||
public Parcelable createFromParcel(Parcel in) { | |||
return in.readParcelable(getClass().getClassLoader()); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, Parcelable val) { | |||
if (val instanceof SuperParcelable) { | |||
((SuperParcelable) val).readFromParcel(in); | |||
return; | |||
} | |||
Method method = Utils.getMethodReadFromParcel(val.getClass()); | |||
if (method == null) { | |||
throw new IllegalArgumentException("Parcelable parameter with @Out or @Inout annotation must " + | |||
"declare the public readFromParcel() method or implements interface SuperParcelable. " + | |||
"Error parameter type: " + val.getClass().getName()); | |||
} | |||
try { | |||
method.invoke(val, in); | |||
} catch (IllegalAccessException e) { | |||
Logger.e(TAG, "Can't access method readFromParcel().", e); | |||
} catch (InvocationTargetException e) { | |||
Logger.e(TAG, "Method readFromParcel() throws an exception.", e); | |||
} | |||
} | |||
} | |||
final class ListType implements OutType<List> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, List val) { | |||
dest.writeList(val); | |||
} | |||
@Override | |||
public List createFromParcel(Parcel in) { | |||
return in.readArrayList(getClass().getClassLoader()); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, List val) { | |||
// Clear the list before read list | |||
val.clear(); | |||
int N = in.readInt(); | |||
while (N > 0) { | |||
Object value = in.readValue(getClass().getClassLoader()); | |||
val.add(value); | |||
N--; | |||
} | |||
} | |||
} | |||
final class MapType implements OutType<Map> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Map val) { | |||
dest.writeMap(val); | |||
} | |||
@Override | |||
public Map createFromParcel(Parcel in) { | |||
return in.readHashMap(getClass().getClassLoader()); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in, Map val) { | |||
ClassLoader loader = getClass().getClassLoader(); | |||
// Clear the map before read map | |||
val.clear(); | |||
int N = in.readInt(); | |||
while (N > 0) { | |||
Object key = in.readValue(loader); | |||
Object value = in.readValue(loader); | |||
val.put(key, value); | |||
N--; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,95 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
import java.lang.reflect.Array; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
/** | |||
* Created by codezjx on 2017/11/30.<br/> | |||
*/ | |||
final class OutTypeWrapper implements BaseTypeWrapper { | |||
private int mType; | |||
private Object mParam; | |||
OutTypeWrapper(Object param, Class<?> mParamType) { | |||
mType = Utils.getTypeByClass(mParamType); | |||
mParam = param; | |||
} | |||
@Override | |||
public int getType() { | |||
return mType; | |||
} | |||
@Override | |||
public Object getParam() { | |||
return mParam; | |||
} | |||
@Override | |||
public int describeContents() { | |||
return 0; | |||
} | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags) { | |||
dest.writeInt(mType); | |||
Type type = TypeFactory.getType(mType); | |||
if (flags == PARCELABLE_WRITE_RETURN_VALUE) { | |||
type.writeToParcel(dest, flags, mParam); | |||
} else { | |||
if (mType == BaseTypeWrapper.TYPE_PARCELABLE) { | |||
dest.writeString(mParam.getClass().getName()); | |||
} else if (mType == BaseTypeWrapper.TYPE_PARCELABLEARRAY) { | |||
dest.writeInt(Array.getLength(mParam)); | |||
dest.writeString(mParam.getClass().getComponentType().getName()); | |||
} else if (Utils.isArrayType(mType)) { | |||
// array type just write length | |||
dest.writeInt(Array.getLength(mParam)); | |||
} | |||
} | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in) { | |||
mType = in.readInt(); | |||
OutType type = (OutType) TypeFactory.getType(mType); | |||
type.readFromParcel(in, mParam); | |||
} | |||
private OutTypeWrapper(Parcel in) { | |||
mType = in.readInt(); | |||
if (mType == BaseTypeWrapper.TYPE_PARCELABLE) { | |||
String clsName = in.readString(); | |||
mParam = Utils.createObjFromClassName(clsName); | |||
} else if (mType == BaseTypeWrapper.TYPE_PARCELABLEARRAY) { | |||
int length = in.readInt(); | |||
String componentType = in.readString(); | |||
mParam = Utils.createArrayFromComponentType(componentType, length); | |||
} else if (mType == BaseTypeWrapper.TYPE_LIST) { | |||
mParam = new ArrayList(); | |||
} else if (mType == BaseTypeWrapper.TYPE_MAP) { | |||
mParam = new HashMap(); | |||
} else if (Utils.isArrayType(mType)) { | |||
int length = in.readInt(); | |||
ArrayType type = (ArrayType) TypeFactory.getType(mType); | |||
mParam = type.newInstance(length); | |||
} | |||
} | |||
public static final Creator<OutTypeWrapper> CREATOR = new Creator<OutTypeWrapper>() { | |||
@Override | |||
public OutTypeWrapper createFromParcel(Parcel source) { | |||
return new OutTypeWrapper(source); | |||
} | |||
@Override | |||
public OutTypeWrapper[] newArray(int size) { | |||
return new OutTypeWrapper[size]; | |||
} | |||
}; | |||
} |
@@ -0,0 +1,80 @@ | |||
package com.codezjx.andlinker; | |||
import com.codezjx.andlinker.annotation.In; | |||
import com.codezjx.andlinker.annotation.Inout; | |||
import com.codezjx.andlinker.annotation.Out; | |||
import java.lang.annotation.Annotation; | |||
/** | |||
* Created by codezjx on 2017/9/17.<br/> | |||
*/ | |||
interface ParameterHandler<T> { | |||
void apply(RequestBuilder builder, T value, int index); | |||
final class CallbackHandler<T> implements ParameterHandler<T> { | |||
Class<?> mParamType; | |||
CallbackHandler(Class<?> paramType) { | |||
mParamType = paramType; | |||
} | |||
@Override | |||
public void apply(RequestBuilder builder, T value, int index) { | |||
Logger.d("CallbackHandler", "ParameterHandler mParamType:" + mParamType + " value:" + value); | |||
String className = mParamType.getSimpleName(); | |||
CallbackTypeWrapper wrapper = new CallbackTypeWrapper(className); | |||
builder.applyWrapper(index, wrapper); | |||
} | |||
} | |||
final class ParamDirectionHandler<T> implements ParameterHandler<T> { | |||
Annotation mAnnotation; | |||
Class<?> mParamType; | |||
ParamDirectionHandler(Annotation annotation, Class<?> paramType) { | |||
mAnnotation = annotation; | |||
mParamType = paramType; | |||
} | |||
@Override | |||
public void apply(RequestBuilder builder, T value, int index) { | |||
Logger.d("ParamDirectionHandler", " mParamType:" + mParamType + " value:" + value + " index:" + index); | |||
if (Utils.canOnlyBeInType(mParamType) && !(mAnnotation instanceof In)) { | |||
throw new IllegalArgumentException("Primitives are in by default, and cannot be otherwise."); | |||
} | |||
BaseTypeWrapper wrapper = null; | |||
if (mAnnotation instanceof In) { | |||
wrapper = new InTypeWrapper(value, mParamType); | |||
} else if (mAnnotation instanceof Out) { | |||
wrapper = new OutTypeWrapper(value, mParamType); | |||
} else if (mAnnotation instanceof Inout) { | |||
wrapper = new InOutTypeWrapper(value, mParamType); | |||
} | |||
builder.applyWrapper(index, wrapper); | |||
} | |||
} | |||
final class DefaultParameterHandler<T> implements ParameterHandler<T> { | |||
Class<?> mParamType; | |||
DefaultParameterHandler(Class<?> paramType) { | |||
mParamType = paramType; | |||
} | |||
@Override | |||
public void apply(RequestBuilder builder, T value, int index) { | |||
if (Utils.canOnlyBeInType(mParamType)) { | |||
InTypeWrapper wrapper = new InTypeWrapper(value, mParamType); | |||
builder.applyWrapper(index, wrapper); | |||
} else { | |||
throw new IllegalArgumentException("Parameter type '" + mParamType.getSimpleName() + "' can be an out type, so you must declare it as @In, @Out or @Inout."); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,131 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.RemoteException; | |||
import static com.codezjx.andlinker.Utils.checkNotNull; | |||
/** | |||
* Created by codezjx on 2017/10/14.<br/> | |||
*/ | |||
final class RemoteCall implements Call<Object> { | |||
private static final String TAG = "RemoteCall"; | |||
private final ITransfer mTransferService; | |||
private final ServiceMethod mServiceMethod; | |||
private final Object[] mArgs; | |||
private final Dispatcher mDispatcher; | |||
private volatile boolean mExecuted; | |||
private volatile boolean mCanceled; | |||
RemoteCall(ITransfer transferService, ServiceMethod serviceMethod, Object[] args, Dispatcher dispatcher) { | |||
mTransferService = transferService; | |||
mServiceMethod = serviceMethod; | |||
mArgs = args; | |||
mDispatcher = dispatcher; | |||
} | |||
private Object executeInternal() throws RemoteException { | |||
if (mTransferService == null) { | |||
Logger.e(TAG, "TransferService null when execute, this may occur if the client is not already bind to the service."); | |||
return null; | |||
} | |||
@SuppressWarnings("unchecked") | |||
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) mServiceMethod.getParameterHandlers(); | |||
int argumentCount = mArgs != null ? mArgs.length : 0; | |||
if (argumentCount != handlers.length) { | |||
throw new IllegalArgumentException("Argument count (" + argumentCount | |||
+ ") doesn't match expected count (" + handlers.length + ")"); | |||
} | |||
RequestBuilder requestBuilder = new RequestBuilder(mServiceMethod.getClassName(), mServiceMethod.getMethodName(), argumentCount, mServiceMethod.isOneWay()); | |||
for (int p = 0; p < argumentCount; p++) { | |||
handlers[p].apply(requestBuilder, mArgs[p], p); | |||
} | |||
Response response = mTransferService.execute(requestBuilder.build()); | |||
// May return null in one-way mode. | |||
if (response == null) { | |||
return null; | |||
} | |||
if (response.getStatusCode() != Response.STATUS_CODE_SUCCESS) { | |||
Logger.e(TAG, "Execute remote call fail: " + response.toString()); | |||
} | |||
return response.getResult(); | |||
} | |||
@Override | |||
public Object execute() { | |||
checkExecuted(); | |||
if (mCanceled) { | |||
Logger.w(TAG, "Already canceled"); | |||
return null; | |||
} | |||
Object result = null; | |||
try { | |||
result = executeInternal(); | |||
} catch (RemoteException e) { | |||
e.printStackTrace(); | |||
} | |||
return result; | |||
} | |||
@Override | |||
public void enqueue(Callback<Object> callback) { | |||
checkNotNull(callback, "callback == null"); | |||
checkExecuted(); | |||
if (mCanceled) { | |||
Logger.w(TAG, "Already canceled"); | |||
return; | |||
} | |||
mDispatcher.enqueue(new AsyncCall(callback)); | |||
} | |||
private void checkExecuted() { | |||
synchronized (this) { | |||
if (mExecuted) { | |||
throw new IllegalStateException("Already executed."); | |||
} | |||
mExecuted = true; | |||
} | |||
} | |||
final class AsyncCall implements Runnable { | |||
private Callback<Object> mCallback; | |||
AsyncCall(Callback<Object> callback) { | |||
mCallback = callback; | |||
} | |||
@Override | |||
public void run() { | |||
try { | |||
Object result = executeInternal(); | |||
mCallback.onResponse(RemoteCall.this, result); | |||
} catch (RemoteException e) { | |||
e.printStackTrace(); | |||
mCallback.onFailure(RemoteCall.this, e); | |||
} | |||
} | |||
} | |||
@Override | |||
public synchronized boolean isExecuted() { | |||
return mExecuted; | |||
} | |||
@Override | |||
public void cancel() { | |||
mCanceled = true; | |||
} | |||
@Override | |||
public boolean isCanceled() { | |||
return mCanceled; | |||
} | |||
} |
@@ -0,0 +1,134 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
import android.os.Parcelable; | |||
import java.lang.reflect.Array; | |||
import java.util.Arrays; | |||
/** | |||
* Created by codezjx on 2017/9/13.<br/> | |||
*/ | |||
final class Request implements SuperParcelable { | |||
private String mTargetClass; | |||
private String mMethodName; | |||
private BaseTypeWrapper[] mArgsWrapper; | |||
// This field use for client slide only | |||
private boolean mOneWay = false; | |||
Request(String targetClass, String methodName, BaseTypeWrapper[] argsWrapper) { | |||
this(targetClass, methodName, argsWrapper, false); | |||
} | |||
Request(String targetClass, String methodName, BaseTypeWrapper[] argsWrapper, boolean oneWay) { | |||
mTargetClass = targetClass; | |||
mMethodName = methodName; | |||
mArgsWrapper = argsWrapper; | |||
mOneWay = oneWay; | |||
} | |||
@Override | |||
public int describeContents() { | |||
return 0; | |||
} | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags) { | |||
dest.writeString(mTargetClass); | |||
dest.writeString(mMethodName); | |||
if (flags == PARCELABLE_WRITE_RETURN_VALUE) { | |||
writeParcelableArrayToParcel(dest, mArgsWrapper, flags); | |||
} else { | |||
dest.writeParcelableArray(mArgsWrapper, flags); | |||
} | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in) { | |||
mTargetClass = in.readString(); | |||
mMethodName = in.readString(); | |||
readParcelableArrayFromParcel(in, mArgsWrapper); | |||
} | |||
private Request(Parcel in) { | |||
mTargetClass = in.readString(); | |||
mMethodName = in.readString(); | |||
mArgsWrapper = readParcelableArray(getClass().getClassLoader(), BaseTypeWrapper.class, in); | |||
} | |||
private <T extends BaseTypeWrapper> void writeParcelableArrayToParcel(Parcel dest, T[] value, int parcelableFlags) { | |||
if (value != null) { | |||
int N = value.length; | |||
dest.writeInt(N); | |||
for (int i = 0; i < N; i++) { | |||
value[i].writeToParcel(dest, parcelableFlags); | |||
} | |||
} else { | |||
dest.writeInt(-1); | |||
} | |||
} | |||
private <T extends BaseTypeWrapper> void readParcelableArrayFromParcel(Parcel in, T[] value) { | |||
int N = in.readInt(); | |||
if (N < 0) { | |||
return; | |||
} | |||
for (int i = 0; i < N; i++) { | |||
value[i].readFromParcel(in); | |||
} | |||
} | |||
/** | |||
* Code from {@link Parcel}.readParcelableArray(ClassLoader loader, Class<T> clazz), it's a hide method. | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
private <T extends Parcelable> T[] readParcelableArray(ClassLoader loader, Class<T> clazz, Parcel in) { | |||
int N = in.readInt(); | |||
if (N < 0) { | |||
return null; | |||
} | |||
T[] p = (T[]) Array.newInstance(clazz, N); | |||
for (int i = 0; i < N; i++) { | |||
p[i] = in.readParcelable(loader); | |||
} | |||
return p; | |||
} | |||
public static final Creator<Request> CREATOR = new Creator<Request>() { | |||
@Override | |||
public Request createFromParcel(Parcel source) { | |||
return new Request(source); | |||
} | |||
@Override | |||
public Request[] newArray(int size) { | |||
return new Request[size]; | |||
} | |||
}; | |||
String getTargetClass() { | |||
return mTargetClass; | |||
} | |||
String getMethodName() { | |||
return mMethodName; | |||
} | |||
boolean isOneWay() { | |||
return mOneWay; | |||
} | |||
BaseTypeWrapper[] getArgsWrapper() { | |||
return mArgsWrapper; | |||
} | |||
@Override | |||
public String toString() { | |||
return "Request{" + | |||
"mTargetClass='" + mTargetClass + '\'' + | |||
", mMethodName='" + mMethodName + '\'' + | |||
", mArgsWrapper=" + Arrays.toString(mArgsWrapper) + | |||
'}'; | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
package com.codezjx.andlinker; | |||
/** | |||
* Created by codezjx on 2017/11/5.<br/> | |||
*/ | |||
final class RequestBuilder { | |||
private String mTargetClass; | |||
private String mMethodName; | |||
private BaseTypeWrapper[] mParameterWrappers; | |||
private boolean mOneWay; | |||
RequestBuilder(String targetClass, String methodName, int argumentCount, boolean oneWay) { | |||
mTargetClass = targetClass; | |||
mMethodName = methodName; | |||
mParameterWrappers = new BaseTypeWrapper[argumentCount]; | |||
mOneWay = oneWay; | |||
} | |||
void applyWrapper(int index, BaseTypeWrapper wrapper) { | |||
if (index < 0 || index >= mParameterWrappers.length) { | |||
throw new IllegalArgumentException("Index out of range."); | |||
} | |||
mParameterWrappers[index] = wrapper; | |||
} | |||
Request build() { | |||
return new Request(mTargetClass, mMethodName, mParameterWrappers, mOneWay); | |||
} | |||
} |
@@ -0,0 +1,80 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
/** | |||
* Created by codezjx on 2017/9/13.<br/> | |||
*/ | |||
final class Response implements SuperParcelable { | |||
static final int STATUS_CODE_SUCCESS = 200; | |||
static final int STATUS_CODE_ILLEGAL_ACCESS = 400; | |||
static final int STATUS_CODE_INVOCATION_FAIL = 401; | |||
static final int STATUS_CODE_NOT_FOUND = 404; | |||
private int mStatusCode; | |||
private String mStatusMessage; | |||
private Object mResult; | |||
Response(int statusCode, String statusMessage, Object result) { | |||
mStatusCode = statusCode; | |||
mStatusMessage = statusMessage; | |||
mResult = result; | |||
} | |||
@Override | |||
public int describeContents() { | |||
return 0; | |||
} | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags) { | |||
dest.writeInt(mStatusCode); | |||
dest.writeString(mStatusMessage); | |||
dest.writeValue(mResult); | |||
} | |||
@Override | |||
public void readFromParcel(Parcel in) { | |||
mStatusCode = in.readInt(); | |||
mStatusMessage = in.readString(); | |||
mResult = in.readValue(getClass().getClassLoader()); | |||
} | |||
private Response(Parcel in) { | |||
readFromParcel(in); | |||
} | |||
public static final Creator<Response> CREATOR = new Creator<Response>() { | |||
@Override | |||
public Response createFromParcel(Parcel source) { | |||
return new Response(source); | |||
} | |||
@Override | |||
public Response[] newArray(int size) { | |||
return new Response[size]; | |||
} | |||
}; | |||
int getStatusCode() { | |||
return mStatusCode; | |||
} | |||
String getStatusMessage() { | |||
return mStatusMessage; | |||
} | |||
Object getResult() { | |||
return mResult; | |||
} | |||
@Override | |||
public String toString() { | |||
return "Response{" + | |||
"mStatusCode=" + mStatusCode + | |||
", mStatusMessage='" + mStatusMessage + '\'' + | |||
", mResult=" + mResult + | |||
'}'; | |||
} | |||
} |
@@ -0,0 +1,159 @@ | |||
package com.codezjx.andlinker; | |||
import com.codezjx.andlinker.annotation.Callback; | |||
import com.codezjx.andlinker.annotation.In; | |||
import com.codezjx.andlinker.annotation.Inout; | |||
import com.codezjx.andlinker.annotation.OneWay; | |||
import com.codezjx.andlinker.annotation.Out; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.Method; | |||
import java.lang.reflect.Type; | |||
/** | |||
* Created by codezjx on 2017/9/14.<br/> | |||
* Adapts an invocation of an interface method into an AIDL call. | |||
*/ | |||
final class ServiceMethod { | |||
private static final String TAG = "ServiceMethod"; | |||
private CallAdapter mCallAdapter; | |||
private String mClassName; | |||
private String mMethodName; | |||
private boolean mOneWay; | |||
private ParameterHandler<?>[] mParameterHandlers; | |||
ServiceMethod(Builder builder) { | |||
mCallAdapter = builder.mCallAdapter; | |||
mClassName = builder.mClassName; | |||
mMethodName = builder.mMethodName; | |||
mOneWay = builder.mOneWay; | |||
mParameterHandlers = builder.mParameterHandlers; | |||
} | |||
CallAdapter getCallAdapter() { | |||
return mCallAdapter; | |||
} | |||
String getClassName() { | |||
return mClassName; | |||
} | |||
String getMethodName() { | |||
return mMethodName; | |||
} | |||
boolean isOneWay() { | |||
return mOneWay; | |||
} | |||
ParameterHandler<?>[] getParameterHandlers() { | |||
return mParameterHandlers; | |||
} | |||
static final class Builder { | |||
private AndLinker mLinker; | |||
private Method mMethod; | |||
private Annotation[] mMethodAnnotations; | |||
private Annotation[][] mParameterAnnotationsArray; | |||
private Type[] mParameterTypes; | |||
private CallAdapter mCallAdapter; | |||
private String mClassName = ""; | |||
private String mMethodName = ""; | |||
private boolean mOneWay = false; | |||
private ParameterHandler<?>[] mParameterHandlers; | |||
Builder(AndLinker linker, Method method) { | |||
mLinker = linker; | |||
mMethod = method; | |||
mMethodAnnotations = method.getAnnotations(); | |||
mParameterAnnotationsArray = method.getParameterAnnotations(); | |||
mParameterTypes = method.getGenericParameterTypes(); | |||
} | |||
ServiceMethod build() { | |||
mCallAdapter = createCallAdapter(); | |||
mClassName = mMethod.getDeclaringClass().getSimpleName(); | |||
mMethodName = mMethod.getName(); | |||
for (Annotation annotation : mMethodAnnotations) { | |||
if (annotation instanceof OneWay) { | |||
mOneWay = true; | |||
break; | |||
} | |||
} | |||
int parameterCount = mParameterAnnotationsArray.length; | |||
mParameterHandlers = new ParameterHandler<?>[parameterCount]; | |||
for (int p = 0; p < parameterCount; p++) { | |||
Type parameterType = mParameterTypes[p]; | |||
if (Utils.hasUnresolvableType(parameterType)) { | |||
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s", | |||
parameterType); | |||
} | |||
Annotation[] parameterAnnotations = mParameterAnnotationsArray[p]; | |||
mParameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); | |||
} | |||
return new ServiceMethod(this); | |||
} | |||
private CallAdapter createCallAdapter() { | |||
Type returnType = mMethod.getGenericReturnType(); | |||
if (Utils.hasUnresolvableType(returnType)) { | |||
throw methodError( | |||
"Method return type must not include a type variable or wildcard: %s", returnType); | |||
} | |||
Annotation[] annotations = mMethod.getAnnotations(); | |||
try { | |||
//noinspection unchecked | |||
return mLinker.findCallAdapter(returnType, annotations); | |||
} catch (RuntimeException e) { // Wide exception range because factories are user code. | |||
throw methodError(e, "Unable to create call adapter for %s", returnType); | |||
} | |||
} | |||
private ParameterHandler<?> parseParameter(int p, Type parameterType, Annotation[] annotations) { | |||
Class<?> rawParameterType = Utils.getRawType(parameterType); | |||
if (annotations == null || annotations.length == 0) { | |||
return new ParameterHandler.DefaultParameterHandler<>(rawParameterType); | |||
} | |||
for (Annotation annotation : annotations) { | |||
if (annotation instanceof Callback) { | |||
return new ParameterHandler.CallbackHandler<>(rawParameterType); | |||
} else if (annotation instanceof In || annotation instanceof Out || annotation instanceof Inout) { | |||
return new ParameterHandler.ParamDirectionHandler<>(annotation, rawParameterType); | |||
} | |||
} | |||
throw parameterError(p, "No support annotation found."); | |||
} | |||
private RuntimeException methodError(String message, Object... args) { | |||
return methodError(null, message, args); | |||
} | |||
private RuntimeException methodError(Throwable cause, String message, Object... args) { | |||
message = String.format(message, args); | |||
return new IllegalArgumentException(message | |||
+ "\n for method " | |||
+ mMethod.getDeclaringClass().getSimpleName() | |||
+ "." | |||
+ mMethod.getName(), cause); | |||
} | |||
private RuntimeException parameterError( | |||
Throwable cause, int p, String message, Object... args) { | |||
return methodError(cause, message + " (parameter #" + (p + 1) + ")", args); | |||
} | |||
private RuntimeException parameterError(int p, String message, Object... args) { | |||
return methodError(message + " (parameter #" + (p + 1) + ")", args); | |||
} | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
import android.os.Parcelable; | |||
/** | |||
* Parcelable parameter classes with @Out or @Inout annotation must inherit from this class. | |||
*/ | |||
public interface SuperParcelable extends Parcelable { | |||
/** | |||
* Reads the parcel contents into this object, so we can restore it. | |||
* @param in The parcel to overwrite this object from. | |||
*/ | |||
void readFromParcel(Parcel in); | |||
} |
@@ -0,0 +1,166 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
import android.text.TextUtils; | |||
/** | |||
* Created by codezjx on 2017/11/28.<br/> | |||
*/ | |||
interface Type<T> { | |||
void writeToParcel(Parcel dest, int flags, T val); | |||
T createFromParcel(Parcel in); | |||
final class EmptyType implements Type<Object> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Object val) { | |||
// Nothing to do | |||
} | |||
@Override | |||
public Object createFromParcel(Parcel in) { | |||
return null; | |||
} | |||
} | |||
final class ByteType implements Type<Byte> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Byte val) { | |||
dest.writeByte(val); | |||
} | |||
@Override | |||
public Byte createFromParcel(Parcel in) { | |||
return in.readByte(); | |||
} | |||
} | |||
final class ShortType implements Type<Short> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Short val) { | |||
dest.writeInt(val.intValue()); | |||
} | |||
@Override | |||
public Short createFromParcel(Parcel in) { | |||
return (short) in.readInt(); | |||
} | |||
} | |||
final class IntType implements Type<Integer> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Integer val) { | |||
dest.writeInt(val); | |||
} | |||
@Override | |||
public Integer createFromParcel(Parcel in) { | |||
return in.readInt(); | |||
} | |||
} | |||
final class LongType implements Type<Long> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Long val) { | |||
dest.writeLong(val); | |||
} | |||
@Override | |||
public Long createFromParcel(Parcel in) { | |||
return in.readLong(); | |||
} | |||
} | |||
final class FloatType implements Type<Float> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Float val) { | |||
dest.writeFloat(val); | |||
} | |||
@Override | |||
public Float createFromParcel(Parcel in) { | |||
return in.readFloat(); | |||
} | |||
} | |||
final class DoubleType implements Type<Double> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Double val) { | |||
dest.writeDouble(val); | |||
} | |||
@Override | |||
public Double createFromParcel(Parcel in) { | |||
return in.readDouble(); | |||
} | |||
} | |||
final class BooleanType implements Type<Boolean> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Boolean val) { | |||
dest.writeInt(val ? 1 : 0); | |||
} | |||
@Override | |||
public Boolean createFromParcel(Parcel in) { | |||
return in.readInt() == 1; | |||
} | |||
} | |||
final class CharType implements Type<Character> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, Character val) { | |||
dest.writeInt(((int) val)); | |||
} | |||
@Override | |||
public Character createFromParcel(Parcel in) { | |||
return (char) in.readInt(); | |||
} | |||
} | |||
final class StringType implements Type<String> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, String val) { | |||
dest.writeString(val); | |||
} | |||
@Override | |||
public String createFromParcel(Parcel in) { | |||
return in.readString(); | |||
} | |||
} | |||
final class CharSequenceType implements Type<CharSequence> { | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags, CharSequence val) { | |||
writeCharSequence(dest, val); | |||
} | |||
@Override | |||
public CharSequence createFromParcel(Parcel in) { | |||
return readCharSequence(in); | |||
} | |||
static void writeCharSequence(Parcel dest, CharSequence val) { | |||
TextUtils.writeToParcel(val, dest, 0); | |||
} | |||
static CharSequence readCharSequence(Parcel in) { | |||
return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); | |||
} | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
package com.codezjx.andlinker; | |||
/** | |||
* Created by codezjx on 2017/11/28.<br/> | |||
*/ | |||
final class TypeFactory { | |||
private TypeFactory() { | |||
} | |||
private static Type[] sTypeArr = { | |||
new Type.EmptyType(), | |||
new Type.ByteType(), new Type.ShortType(), new Type.IntType(), new Type.LongType(), | |||
new Type.FloatType(), new Type.DoubleType(), new Type.BooleanType(), new Type.CharType(), | |||
new ArrayType.ByteArrayType(), new ArrayType.ShortArrayType(), new ArrayType.IntArrayType(), new ArrayType.LongArrayType(), | |||
new ArrayType.FloatArrayType(), new ArrayType.DoubleArrayType(), new ArrayType.BooleanArrayType(), new ArrayType.CharArrayType(), | |||
new Type.StringType(), new ArrayType.StringArrayType(), new Type.CharSequenceType(), new ArrayType.CharSequenceArrayType(), | |||
new OutType.ParcelableType(), new ArrayType.ParcelableArrayType(), new OutType.ListType(), new OutType.MapType() | |||
}; | |||
static Type getType(int type) { | |||
return sTypeArr[type]; | |||
} | |||
static Type getType(Class<?> classType) { | |||
return sTypeArr[Utils.getTypeByClass(classType)]; | |||
} | |||
} |
@@ -0,0 +1,227 @@ | |||
package com.codezjx.andlinker; | |||
import android.os.Parcel; | |||
import android.os.Parcelable; | |||
import java.lang.reflect.Array; | |||
import java.lang.reflect.GenericArrayType; | |||
import java.lang.reflect.Method; | |||
import java.lang.reflect.ParameterizedType; | |||
import java.lang.reflect.Type; | |||
import java.lang.reflect.TypeVariable; | |||
import java.lang.reflect.WildcardType; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* Created by codezjx on 2017/9/14.<br/> | |||
*/ | |||
final class Utils { | |||
private static final String TAG = "Utils"; | |||
private Utils() { | |||
// private constructor | |||
} | |||
static <T> void validateServiceInterface(Class<T> service) { | |||
if (!service.isInterface()) { | |||
throw new IllegalArgumentException("API declarations must be interfaces."); | |||
} | |||
// Prevent API interfaces from extending other interfaces. This not only avoids a bug in | |||
// Android (http://b.android.com/58753) but it forces composition of API declarations which is | |||
// the recommended pattern. | |||
if (service.getInterfaces().length > 0) { | |||
throw new IllegalArgumentException("API interfaces must not extend other interfaces."); | |||
} | |||
} | |||
static boolean hasUnresolvableType(Type type) { | |||
if (type instanceof Class<?>) { | |||
return false; | |||
} | |||
if (type instanceof ParameterizedType) { | |||
ParameterizedType parameterizedType = (ParameterizedType) type; | |||
for (Type typeArgument : parameterizedType.getActualTypeArguments()) { | |||
if (hasUnresolvableType(typeArgument)) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
if (type instanceof GenericArrayType) { | |||
return hasUnresolvableType(((GenericArrayType) type).getGenericComponentType()); | |||
} | |||
if (type instanceof TypeVariable) { | |||
return true; | |||
} | |||
if (type instanceof WildcardType) { | |||
return true; | |||
} | |||
String className = type == null ? "null" : type.getClass().getName(); | |||
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " | |||
+ "GenericArrayType, but <" + type + "> is of type " + className); | |||
} | |||
static Class<?> getRawType(Type type) { | |||
checkNotNull(type, "type == null"); | |||
if (type instanceof Class<?>) { | |||
// Type is a normal class. | |||
return (Class<?>) type; | |||
} | |||
if (type instanceof ParameterizedType) { | |||
ParameterizedType parameterizedType = (ParameterizedType) type; | |||
// I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but | |||
// suspects some pathological case related to nested classes exists. | |||
Type rawType = parameterizedType.getRawType(); | |||
if (!(rawType instanceof Class)) throw new IllegalArgumentException(); | |||
return (Class<?>) rawType; | |||
} | |||
if (type instanceof GenericArrayType) { | |||
Type componentType = ((GenericArrayType) type).getGenericComponentType(); | |||
return Array.newInstance(getRawType(componentType), 0).getClass(); | |||
} | |||
if (type instanceof TypeVariable) { | |||
// We could use the variable's bounds, but that won't work if there are multiple. Having a raw | |||
// type that's more general than necessary is okay. | |||
return Object.class; | |||
} | |||
if (type instanceof WildcardType) { | |||
return getRawType(((WildcardType) type).getUpperBounds()[0]); | |||
} | |||
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " | |||
+ "GenericArrayType, but <" + type + "> is of type " + type.getClass().getName()); | |||
} | |||
static <T> T checkNotNull(T object, String message) { | |||
if (object == null) { | |||
throw new NullPointerException(message); | |||
} | |||
return object; | |||
} | |||
static boolean canOnlyBeInType(Class<?> classType) { | |||
return isPrimitiveType(classType) || classType == String.class || classType == CharSequence.class; | |||
} | |||
static boolean isArrayType(int type) { | |||
return type == BaseTypeWrapper.TYPE_BYTEARRAY || type == BaseTypeWrapper.TYPE_SHORTARRAY || type == BaseTypeWrapper.TYPE_INTARRAY | |||
|| type == BaseTypeWrapper.TYPE_LONGARRAY || type == BaseTypeWrapper.TYPE_FLOATARRAY || type == BaseTypeWrapper.TYPE_DOUBLEARRAY | |||
|| type == BaseTypeWrapper.TYPE_BOOLEANARRAY || type == BaseTypeWrapper.TYPE_CHARARRAY || type == BaseTypeWrapper.TYPE_STRINGARRAY | |||
|| type == BaseTypeWrapper.TYPE_CHARSEQUENCEARRAY || type == BaseTypeWrapper.TYPE_PARCELABLEARRAY; | |||
} | |||
static boolean isPrimitiveType(Class<?> classType) { | |||
return classType.isPrimitive() || classType == Byte.class || classType == Short.class | |||
|| classType == Integer.class || classType == Long.class || classType == Float.class | |||
|| classType == Double.class || classType == Boolean.class || classType == Character.class; | |||
} | |||
static boolean isArrayType(Class<?> classType) { | |||
return classType == byte[].class || classType == short[].class || classType == int[].class || classType == long[].class | |||
|| classType == float[].class || classType == double[].class || classType == boolean[].class || classType == char[].class | |||
|| classType == String[].class || classType == CharSequence[].class || classType == Parcelable[].class; | |||
} | |||
static int getTypeByClass(Class<?> classType) { | |||
int type; | |||
if (classType == byte.class || classType == Byte.class) { | |||
type = BaseTypeWrapper.TYPE_BYTE; | |||
} else if (classType == short.class || classType == Short.class) { | |||
type = BaseTypeWrapper.TYPE_SHORT; | |||
} else if (classType == int.class || classType == Integer.class) { | |||
type = BaseTypeWrapper.TYPE_INT; | |||
} else if (classType == long.class || classType == Long.class) { | |||
type = BaseTypeWrapper.TYPE_LONG; | |||
} else if (classType == float.class || classType == Float.class) { | |||
type = BaseTypeWrapper.TYPE_FLOAT; | |||
} else if (classType == double.class || classType == Double.class) { | |||
type = BaseTypeWrapper.TYPE_DOUBLE; | |||
} else if (classType == boolean.class || classType == Boolean.class) { | |||
type = BaseTypeWrapper.TYPE_BOOLEAN; | |||
} else if (classType == char.class || classType == Character.class) { | |||
type = BaseTypeWrapper.TYPE_CHAR; | |||
} else if (classType == byte[].class) { | |||
type = BaseTypeWrapper.TYPE_BYTEARRAY; | |||
} else if (classType == short[].class) { | |||
type = BaseTypeWrapper.TYPE_SHORTARRAY; | |||
} else if (classType == int[].class) { | |||
type = BaseTypeWrapper.TYPE_INTARRAY; | |||
} else if (classType == long[].class) { | |||
type = BaseTypeWrapper.TYPE_LONGARRAY; | |||
} else if (classType == float[].class) { | |||
type = BaseTypeWrapper.TYPE_FLOATARRAY; | |||
} else if (classType == double[].class) { | |||
type = BaseTypeWrapper.TYPE_DOUBLEARRAY; | |||
} else if (classType == boolean[].class) { | |||
type = BaseTypeWrapper.TYPE_BOOLEANARRAY; | |||
} else if (classType == char[].class) { | |||
type = BaseTypeWrapper.TYPE_CHARARRAY; | |||
} else if (classType == String.class) { | |||
type = BaseTypeWrapper.TYPE_STRING; | |||
} else if (classType == String[].class) { | |||
type = BaseTypeWrapper.TYPE_STRINGARRAY; | |||
} else if (classType == CharSequence.class) { | |||
type = BaseTypeWrapper.TYPE_CHARSEQUENCE; | |||
} else if (classType == CharSequence[].class) { | |||
type = BaseTypeWrapper.TYPE_CHARSEQUENCEARRAY; | |||
} else if (Parcelable.class.isAssignableFrom(classType)) { | |||
type = BaseTypeWrapper.TYPE_PARCELABLE; | |||
} else if (Parcelable[].class.isAssignableFrom(classType)) { | |||
type = BaseTypeWrapper.TYPE_PARCELABLEARRAY; | |||
} else if (List.class.isAssignableFrom(classType)) { | |||
type = BaseTypeWrapper.TYPE_LIST; | |||
} else if (Map.class.isAssignableFrom(classType)) { | |||
type = BaseTypeWrapper.TYPE_MAP; | |||
} else { | |||
type = BaseTypeWrapper.TYPE_EMPTY; | |||
} | |||
return type; | |||
} | |||
static Object createObjFromClassName(String clsName) { | |||
Object obj = null; | |||
try { | |||
obj = Class.forName(clsName).newInstance(); | |||
} catch (InstantiationException e) { | |||
e.printStackTrace(); | |||
} catch (IllegalAccessException e) { | |||
e.printStackTrace(); | |||
} catch (ClassNotFoundException e) { | |||
e.printStackTrace(); | |||
} | |||
return obj; | |||
} | |||
static Object createArrayFromComponentType(String componentType, int length) { | |||
Object obj = null; | |||
try { | |||
Class clsType = Class.forName(componentType); | |||
obj = Array.newInstance(clsType, length); | |||
} catch (ClassNotFoundException e) { | |||
e.printStackTrace(); | |||
} | |||
return obj; | |||
} | |||
static boolean isStringBlank(String str) { | |||
return str == null || str.trim().length() == 0; | |||
} | |||
static Method getMethodReadFromParcel(Class<?> cls) { | |||
checkNotNull(cls, "Class must not be null."); | |||
Method method = null; | |||
try { | |||
method = cls.getMethod("readFromParcel", Parcel.class); | |||
} catch (NoSuchMethodException e) { | |||
Logger.e(TAG, "No public readFromParcel() method in class:" + cls.getName()); | |||
} catch (SecurityException e) { | |||
Logger.e(TAG, "SecurityException when get readFromParcel() method in class:" + cls.getName()); | |||
} | |||
return method; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
package com.codezjx.andlinker.adapter; | |||
import com.codezjx.andlinker.Call; | |||
import com.codezjx.andlinker.CallAdapter; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.Type; | |||
/** | |||
* Default {@linkplain CallAdapter.Factory call adapter} which adapt {@link Call} to the execute result. | |||
*/ | |||
public class DefaultCallAdapterFactory extends CallAdapter.Factory { | |||
public static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory(); | |||
@Override | |||
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations) { | |||
return new CallAdapter<Object, Object>() { | |||
@Override | |||
public Object adapt(Call<Object> call) { | |||
// Return the result | |||
return call.execute(); | |||
} | |||
}; | |||
} | |||
} |
@@ -0,0 +1,125 @@ | |||
package com.codezjx.andlinker.adapter; | |||
import android.os.Handler; | |||
import android.os.Looper; | |||
import com.codezjx.andlinker.Call; | |||
import com.codezjx.andlinker.CallAdapter; | |||
import com.codezjx.andlinker.Callback; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.Type; | |||
import java.util.concurrent.Executor; | |||
/** | |||
* A {@linkplain CallAdapter.Factory call adapter} which uses the original {@link Call}, just return as is. | |||
*/ | |||
public class OriginalCallAdapterFactory extends CallAdapter.Factory { | |||
private Executor mCallbackExecutor; | |||
private OriginalCallAdapterFactory(Executor callbackExecutor) { | |||
mCallbackExecutor = callbackExecutor; | |||
} | |||
/** | |||
* Create {@link OriginalCallAdapterFactory} with default Android main thread executor. | |||
*/ | |||
public static OriginalCallAdapterFactory create() { | |||
return new OriginalCallAdapterFactory(new MainThreadExecutor()); | |||
} | |||
/** | |||
* Create {@link OriginalCallAdapterFactory} with specify {@link Executor} | |||
* @param callbackExecutor The executor on which {@link Callback} methods are invoked | |||
* when returning {@link Call} from your service method. | |||
*/ | |||
public static OriginalCallAdapterFactory create(Executor callbackExecutor) { | |||
return new OriginalCallAdapterFactory(callbackExecutor); | |||
} | |||
@Override | |||
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations) { | |||
if (getRawType(returnType) != Call.class) { | |||
return null; | |||
} | |||
return new CallAdapter<Object, Call<?>>() { | |||
@Override public Call<Object> adapt(Call<Object> call) { | |||
// Return executor wrapper call | |||
return new ExecutorCallbackCall<>(mCallbackExecutor, call); | |||
} | |||
}; | |||
} | |||
static final class ExecutorCallbackCall<T> implements Call<T> { | |||
final Executor mCallbackExecutor; | |||
final Call<T> mDelegate; | |||
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { | |||
mCallbackExecutor = callbackExecutor; | |||
mDelegate = delegate; | |||
} | |||
@Override | |||
public T execute() { | |||
return mDelegate.execute(); | |||
} | |||
@Override | |||
public void enqueue(final Callback<T> callback) { | |||
if (callback == null) { | |||
throw new NullPointerException("callback == null"); | |||
} | |||
mDelegate.enqueue(new Callback<T>() { | |||
@Override | |||
public void onResponse(Call<T> call, final T response) { | |||
mCallbackExecutor.execute(new Runnable() { | |||
@Override | |||
public void run() { | |||
if (mDelegate.isCanceled()) { | |||
// Emulate behavior of throwing/delivering an IOException on cancellation. | |||
callback.onFailure(ExecutorCallbackCall.this, new IllegalStateException("Already canceled")); | |||
} else { | |||
callback.onResponse(ExecutorCallbackCall.this, response); | |||
} | |||
} | |||
}); | |||
} | |||
@Override | |||
public void onFailure(Call<T> call, final Throwable t) { | |||
mCallbackExecutor.execute(new Runnable() { | |||
@Override | |||
public void run() { | |||
callback.onFailure(ExecutorCallbackCall.this, t); | |||
} | |||
}); | |||
} | |||
}); | |||
} | |||
@Override | |||
public boolean isExecuted() { | |||
return mDelegate.isExecuted(); | |||
} | |||
@Override | |||
public void cancel() { | |||
mDelegate.cancel(); | |||
} | |||
@Override | |||
public boolean isCanceled() { | |||
return mDelegate.isCanceled(); | |||
} | |||
} | |||
private static class MainThreadExecutor implements Executor { | |||
private final Handler handler = new Handler(Looper.getMainLooper()); | |||
@Override public void execute(Runnable r) { | |||
handler.post(r); | |||
} | |||
} | |||
} |
@@ -0,0 +1,163 @@ | |||
package com.codezjx.andlinker.adapter.rxjava; | |||
import com.codezjx.andlinker.Call; | |||
import java.util.concurrent.atomic.AtomicInteger; | |||
import rx.Producer; | |||
import rx.Subscriber; | |||
import rx.Subscription; | |||
import rx.exceptions.CompositeException; | |||
import rx.exceptions.Exceptions; | |||
import rx.exceptions.OnCompletedFailedException; | |||
import rx.exceptions.OnErrorFailedException; | |||
import rx.exceptions.OnErrorNotImplementedException; | |||
import rx.plugins.RxJavaPlugins; | |||
/** | |||
* Created by codezjx on 2017/10/16.<br/> | |||
*/ | |||
final class CallArbiter<T> extends AtomicInteger implements Subscription, Producer { | |||
private static final int STATE_WAITING = 0; | |||
private static final int STATE_REQUESTED = 1; | |||
private static final int STATE_HAS_RESPONSE = 2; | |||
private static final int STATE_TERMINATED = 3; | |||
private final Call<T> mCall; | |||
private final Subscriber<? super T> mSubscriber; | |||
private volatile T mResponse; | |||
CallArbiter(Call<T> call, Subscriber<? super T> subscriber) { | |||
super(STATE_WAITING); | |||
mCall = call; | |||
mSubscriber = subscriber; | |||
} | |||
@Override | |||
public void unsubscribe() { | |||
mCall.cancel(); | |||
} | |||
@Override | |||
public boolean isUnsubscribed() { | |||
return mCall.isCanceled(); | |||
} | |||
@Override | |||
public void request(long amount) { | |||
if (amount == 0) { | |||
return; | |||
} | |||
while (true) { | |||
int state = get(); | |||
switch (state) { | |||
case STATE_WAITING: | |||
if (compareAndSet(STATE_WAITING, STATE_REQUESTED)) { | |||
return; | |||
} | |||
break; // State transition failed. Try again. | |||
case STATE_HAS_RESPONSE: | |||
if (compareAndSet(STATE_HAS_RESPONSE, STATE_TERMINATED)) { | |||
deliverResponse(mResponse); | |||
return; | |||
} | |||
break; // State transition failed. Try again. | |||
case STATE_REQUESTED: | |||
case STATE_TERMINATED: | |||
return; // Nothing to do. | |||
default: | |||
throw new IllegalStateException("Unknown state: " + state); | |||
} | |||
} | |||
} | |||
void emitResponse(T response) { | |||
while (true) { | |||
int state = get(); | |||
switch (state) { | |||
case STATE_WAITING: | |||
this.mResponse = response; | |||
if (compareAndSet(STATE_WAITING, STATE_HAS_RESPONSE)) { | |||
return; | |||
} | |||
break; // State transition failed. Try again. | |||
case STATE_REQUESTED: | |||
if (compareAndSet(STATE_REQUESTED, STATE_TERMINATED)) { | |||
deliverResponse(response); | |||
return; | |||
} | |||
break; // State transition failed. Try again. | |||
case STATE_HAS_RESPONSE: | |||
case STATE_TERMINATED: | |||
throw new AssertionError(); | |||
default: | |||
throw new IllegalStateException("Unknown state: " + state); | |||
} | |||
} | |||
} | |||
private void deliverResponse(T response) { | |||
try { | |||
if (!isUnsubscribed()) { | |||
mSubscriber.onNext(response); | |||
} | |||
} catch (OnCompletedFailedException | |||
| OnErrorFailedException | |||
| OnErrorNotImplementedException e) { | |||
RxJavaPlugins.getInstance().getErrorHandler().handleError(e); | |||
return; | |||
} catch (Throwable t) { | |||
Exceptions.throwIfFatal(t); | |||
try { | |||
mSubscriber.onError(t); | |||
} catch (OnCompletedFailedException | |||
| OnErrorFailedException | |||
| OnErrorNotImplementedException e) { | |||
RxJavaPlugins.getInstance().getErrorHandler().handleError(e); | |||
} catch (Throwable inner) { | |||
Exceptions.throwIfFatal(inner); | |||
CompositeException composite = new CompositeException(t, inner); | |||
RxJavaPlugins.getInstance().getErrorHandler().handleError(composite); | |||
} | |||
return; | |||
} | |||
try { | |||
if (!isUnsubscribed()) { | |||
mSubscriber.onCompleted(); | |||
} | |||
} catch (OnCompletedFailedException | |||
| OnErrorFailedException | |||
| OnErrorNotImplementedException e) { | |||
RxJavaPlugins.getInstance().getErrorHandler().handleError(e); | |||
} catch (Throwable t) { | |||
Exceptions.throwIfFatal(t); | |||
RxJavaPlugins.getInstance().getErrorHandler().handleError(t); | |||
} | |||
} | |||
void emitError(Throwable t) { | |||
set(STATE_TERMINATED); | |||
if (!isUnsubscribed()) { | |||
try { | |||
mSubscriber.onError(t); | |||
} catch (OnCompletedFailedException | |||
| OnErrorFailedException | |||
| OnErrorNotImplementedException e) { | |||
RxJavaPlugins.getInstance().getErrorHandler().handleError(e); | |||
} catch (Throwable inner) { | |||
Exceptions.throwIfFatal(inner); | |||
CompositeException composite = new CompositeException(t, inner); | |||
RxJavaPlugins.getInstance().getErrorHandler().handleError(composite); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
package com.codezjx.andlinker.adapter.rxjava; | |||
import com.codezjx.andlinker.Call; | |||
import rx.Observable.OnSubscribe; | |||
import rx.Subscriber; | |||
import rx.exceptions.Exceptions; | |||
/** | |||
* Created by codezjx on 2017/10/16.<br/> | |||
*/ | |||
final class CallExecuteOnSubscribe<T> implements OnSubscribe<T> { | |||
private final Call<T> mCall; | |||
CallExecuteOnSubscribe(Call<T> call) { | |||
mCall = call; | |||
} | |||
@Override | |||
public void call(Subscriber<? super T> subscriber) { | |||
CallArbiter<T> arbiter = new CallArbiter<>(mCall, subscriber); | |||
subscriber.add(arbiter); | |||
subscriber.setProducer(arbiter); | |||
T response; | |||
try { | |||
response = mCall.execute(); | |||
} catch (Throwable t) { | |||
Exceptions.throwIfFatal(t); | |||
arbiter.emitError(t); | |||
return; | |||
} | |||
arbiter.emitResponse(response); | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
package com.codezjx.andlinker.adapter.rxjava; | |||
import com.codezjx.andlinker.Call; | |||
import com.codezjx.andlinker.CallAdapter; | |||
import rx.Observable; | |||
import rx.Observable.OnSubscribe; | |||
import rx.Scheduler; | |||
/** | |||
* Created by codezjx on 2017/10/16.<br/> | |||
*/ | |||
final class RxJavaCallAdapter<R> implements CallAdapter<R, Observable<R>> { | |||
private final Scheduler mScheduler; | |||
RxJavaCallAdapter(Scheduler scheduler) { | |||
mScheduler = scheduler; | |||
} | |||
@Override | |||
public Observable<R> adapt(Call<R> call) { | |||
OnSubscribe<R> subscribe = new CallExecuteOnSubscribe<>(call); | |||
Observable<R> observable = Observable.create(subscribe); | |||
if (mScheduler != null) { | |||
observable = observable.subscribeOn(mScheduler); | |||
} | |||
return observable; | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
package com.codezjx.andlinker.adapter.rxjava; | |||
import com.codezjx.andlinker.CallAdapter; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.ParameterizedType; | |||
import java.lang.reflect.Type; | |||
import rx.Observable; | |||
import rx.Scheduler; | |||
/** | |||
* A {@linkplain CallAdapter.Factory call adapter} which uses RxJava for creating observables. | |||
*/ | |||
public class RxJavaCallAdapterFactory extends CallAdapter.Factory { | |||
private final Scheduler mScheduler; | |||
private RxJavaCallAdapterFactory(Scheduler scheduler) { | |||
mScheduler = scheduler; | |||
} | |||
public static RxJavaCallAdapterFactory create() { | |||
return new RxJavaCallAdapterFactory(null); | |||
} | |||
public static RxJavaCallAdapterFactory createWithScheduler(Scheduler scheduler) { | |||
if (scheduler == null) throw new NullPointerException("scheduler == null"); | |||
return new RxJavaCallAdapterFactory(scheduler); | |||
} | |||
@Override | |||
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations) { | |||
Class<?> rawType = getRawType(returnType); | |||
if (rawType != Observable.class) { | |||
return null; | |||
} | |||
if (!(returnType instanceof ParameterizedType)) { | |||
throw new IllegalStateException("Observable return type must be parameterized" | |||
+ " as Observable<Foo> or Observable<? extends Foo>"); | |||
} | |||
return new RxJavaCallAdapter(mScheduler); | |||
} | |||
} |
@@ -0,0 +1,66 @@ | |||
package com.codezjx.andlinker.adapter.rxjava2; | |||
import com.codezjx.andlinker.Call; | |||
import io.reactivex.Observable; | |||
import io.reactivex.Observer; | |||
import io.reactivex.disposables.Disposable; | |||
import io.reactivex.exceptions.CompositeException; | |||
import io.reactivex.exceptions.Exceptions; | |||
import io.reactivex.plugins.RxJavaPlugins; | |||
/** | |||
* Created by codezjx on 2017/10/14.<br/> | |||
*/ | |||
final class CallExecuteObservable<T> extends Observable<T> { | |||
private final Call<T> mCall; | |||
CallExecuteObservable(Call<T> call) { | |||
mCall = call; | |||
} | |||
@Override | |||
protected void subscribeActual(Observer<? super T> observer) { | |||
observer.onSubscribe(new CallDisposable(mCall)); | |||
boolean terminated = false; | |||
try { | |||
T response = mCall.execute(); | |||
if (!mCall.isCanceled()) { | |||
observer.onNext(response); | |||
} | |||
if (!mCall.isCanceled()) { | |||
terminated = true; | |||
observer.onComplete(); | |||
} | |||
} catch (Throwable t) { | |||
Exceptions.throwIfFatal(t); | |||
if (terminated) { | |||
RxJavaPlugins.onError(t); | |||
} else if (!mCall.isCanceled()) { | |||
try { | |||
observer.onError(t); | |||
} catch (Throwable inner) { | |||
Exceptions.throwIfFatal(inner); | |||
RxJavaPlugins.onError(new CompositeException(t, inner)); | |||
} | |||
} | |||
} | |||
} | |||
private static final class CallDisposable implements Disposable { | |||
private final Call<?> mCall; | |||
CallDisposable(Call<?> call) { | |||
this.mCall = call; | |||
} | |||
@Override public void dispose() { | |||
mCall.cancel(); | |||
} | |||
@Override public boolean isDisposed() { | |||
return mCall.isCanceled(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
package com.codezjx.andlinker.adapter.rxjava2; | |||
import com.codezjx.andlinker.Call; | |||
import com.codezjx.andlinker.CallAdapter; | |||
import io.reactivex.BackpressureStrategy; | |||
import io.reactivex.Observable; | |||
import io.reactivex.Scheduler; | |||
/** | |||
* Created by codezjx on 2017/10/14.<br/> | |||
*/ | |||
final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> { | |||
private final Scheduler mScheduler; | |||
private final boolean mIsFlowable; | |||
RxJava2CallAdapter(Scheduler scheduler, boolean isFlowable) { | |||
mScheduler = scheduler; | |||
mIsFlowable = isFlowable; | |||
} | |||
@Override | |||
public Object adapt(Call<R> call) { | |||
Observable<R> observable = new CallExecuteObservable<>(call); | |||
if (mScheduler != null) { | |||
observable = observable.subscribeOn(mScheduler); | |||
} | |||
if (mIsFlowable) { | |||
return observable.toFlowable(BackpressureStrategy.LATEST); | |||
} | |||
return observable; | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
package com.codezjx.andlinker.adapter.rxjava2; | |||
import com.codezjx.andlinker.CallAdapter; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.ParameterizedType; | |||
import java.lang.reflect.Type; | |||
import io.reactivex.Flowable; | |||
import io.reactivex.Observable; | |||
import io.reactivex.Scheduler; | |||
/** | |||
* A {@linkplain CallAdapter.Factory call adapter} which uses RxJava 2 for creating observables. | |||
*/ | |||
public class RxJava2CallAdapterFactory extends CallAdapter.Factory { | |||
private final Scheduler mScheduler; | |||
private RxJava2CallAdapterFactory(Scheduler scheduler) { | |||
mScheduler = scheduler; | |||
} | |||
public static RxJava2CallAdapterFactory create() { | |||
return new RxJava2CallAdapterFactory(null); | |||
} | |||
public static RxJava2CallAdapterFactory createWithScheduler(Scheduler scheduler) { | |||
if (scheduler == null) throw new NullPointerException("scheduler == null"); | |||
return new RxJava2CallAdapterFactory(scheduler); | |||
} | |||
@Override | |||
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations) { | |||
Class<?> rawType = getRawType(returnType); | |||
boolean isFlowable = (rawType == Flowable.class); | |||
if (rawType != Observable.class && !isFlowable) { | |||
return null; | |||
} | |||
if (!(returnType instanceof ParameterizedType)) { | |||
String name = isFlowable ? "Flowable" : "Observable"; | |||
throw new IllegalStateException(name + " return type must be parameterized" | |||
+ " as " + name + "<Foo> or " + name + "<? extends Foo>"); | |||
} | |||
return new RxJava2CallAdapter(mScheduler, isFlowable); | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.codezjx.andlinker.annotation; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.Target; | |||
import static java.lang.annotation.ElementType.PARAMETER; | |||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | |||
/** | |||
* Indicate a parameter is a remote callback type. | |||
*/ | |||
@Target(PARAMETER) | |||
@Retention(RUNTIME) | |||
public @interface Callback { | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.codezjx.andlinker.annotation; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.Target; | |||
import static java.lang.annotation.ElementType.PARAMETER; | |||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | |||
/** | |||
* Directional tag indicating which way the data goes, same as "in" tag in AIDL. | |||
*/ | |||
@Target(PARAMETER) | |||
@Retention(RUNTIME) | |||
public @interface In { | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.codezjx.andlinker.annotation; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.Target; | |||
import static java.lang.annotation.ElementType.PARAMETER; | |||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | |||
/** | |||
* Directional tag indicating which way the data goes, same as "inout" tag in AIDL. | |||
*/ | |||
@Target(PARAMETER) | |||
@Retention(RUNTIME) | |||
public @interface Inout { | |||
} |
@@ -0,0 +1,17 @@ | |||
package com.codezjx.andlinker.annotation; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.Target; | |||
import static java.lang.annotation.ElementType.METHOD; | |||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | |||
/** | |||
* Indicate a remote call does not block, it simply sends the transaction data and immediately | |||
* returns, same as "oneway" tag in AIDL. | |||
*/ | |||
@Target(METHOD) | |||
@Retention(RUNTIME) | |||
public @interface OneWay { | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.codezjx.andlinker.annotation; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.Target; | |||
import static java.lang.annotation.ElementType.PARAMETER; | |||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | |||
/** | |||
* Directional tag indicating which way the data goes, same as "out" tag in AIDL. | |||
*/ | |||
@Target(PARAMETER) | |||
@Retention(RUNTIME) | |||
public @interface Out { | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.codezjx.andlinker.annotation; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.Target; | |||
import static java.lang.annotation.ElementType.TYPE; | |||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | |||
/** | |||
* Specify the interface as remote service interface. | |||
*/ | |||
@Target(TYPE) | |||
@Retention(RUNTIME) | |||
public @interface RemoteInterface { | |||
} |
@@ -0,0 +1 @@ | |||
/build |
@@ -0,0 +1,80 @@ | |||
apply plugin: 'com.android.application' | |||
android { | |||
compileSdkVersion 26 | |||
buildToolsVersion '26.0.2' | |||
defaultConfig { | |||
applicationId "com.neusoft.possale" | |||
minSdkVersion 21 | |||
targetSdkVersion 23 | |||
versionCode 7 | |||
versionName "20201123163124" | |||
// jackOptions.enabled = true | |||
javaCompileOptions { | |||
annotationProcessorOptions { | |||
includeCompileClasspath = true | |||
} | |||
} | |||
} | |||
// compileOptions{ | |||
// sourceCompatibility org.gradle.api.JavaVersion.VERSION_1_8 | |||
// targetCompatibility org.gradle.api.JavaVersion.VERSION_1_8 | |||
// } | |||
packagingOptions { | |||
exclude 'META-INF/rxjava.properties' | |||
// ... and any other excludes go here | |||
} | |||
buildTypes { | |||
release { | |||
minifyEnabled false | |||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | |||
} | |||
// debug { | |||
// applicationIdSuffix "debug" | |||
// } | |||
} | |||
sourceSets { main { assets.srcDirs = ['src/main/assets', 'src/main/assets/'] } } | |||
sourceSets { | |||
main { | |||
aidl.srcDirs = ['src/main/aidl/'] | |||
java.srcDirs = ['src/main/java', 'src/main/aidl'] | |||
resources.srcDirs = ['src/main/java', 'src/main/aidl'] | |||
} | |||
} | |||
sourceSets { | |||
main { | |||
jniLibs.srcDirs = ['libs'] | |||
} | |||
} | |||
} | |||
dependencies { | |||
compile fileTree(include: ['*.jar'], dir: 'libs') | |||
testCompile 'junit:junit:4.12' | |||
compile 'com.android.support:design:23.4.0' | |||
//rxlifecycle | |||
compile 'com.trello:rxlifecycle-components:1.0' | |||
//rx | |||
compile 'io.reactivex:rxandroid:1.2.1' | |||
compile 'io.reactivex:rxjava:1.3.0' | |||
//net | |||
compile 'com.squareup.retrofit2:retrofit:2.4.0' | |||
compile 'com.squareup.retrofit2:adapter-rxjava:2.4.0' | |||
compile 'com.squareup.retrofit2:converter-gson:2.4.0' | |||
compile 'com.squareup.okhttp3:logging-interceptor:3.10.0' | |||
compile 'com.google.code.gson:gson:2.7' | |||
compile 'com.github.bumptech.glide:glide:3.7.0' | |||
compile project(path: ':cardreader') | |||
compile project(path: ':barcodescaner') | |||
compile files('libs/butterknife-7.0.1.jar') | |||
compile project(path: ':lib') | |||
compile 'com.ttsea.jrxbus2:jrxbus2:1.0.3' | |||
compile project(path: ':extendMediaPlay') | |||
provided files('libs_ext/androidsim.jar') | |||
implementation 'com.codezjx.library:andlinker:0.7.1' | |||
} |
@@ -0,0 +1,17 @@ | |||
# Add project specific ProGuard rules here. | |||
# By default, the flags in this file are appended to flags specified | |||
# in E:\angel\Cmread_android_studio_sdk\android-sdk-windows/tools/proguard/proguard-android.txt | |||
# You can edit the include path and order by changing the proguardFiles | |||
# directive in build.gradle. | |||
# | |||
# For more details, see | |||
# http://developer.android.com/guide/developing/tools/proguard.html | |||
# Add any project specific keep options here: | |||
# If your project uses WebView with JS, uncomment the following | |||
# and specify the fully qualified class name to the JavaScript interface | |||
# class: | |||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | |||
# public *; | |||
#} |
@@ -0,0 +1,13 @@ | |||
package com.neusoft.possale; | |||
import android.app.Application; | |||
import android.test.ApplicationTestCase; | |||
/** | |||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> | |||
*/ | |||
public class ApplicationTest extends ApplicationTestCase<Application> { | |||
public ApplicationTest() { | |||
super(Application.class); | |||
} | |||
} |
@@ -0,0 +1,266 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||
package="com.neusoft.possale"> | |||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> | |||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> | |||
<uses-permission android:name="android.permission.KOOLCLOUD_CONTACTLESS_CARD" /> | |||
<uses-permission android:name="android.permission.INTERNET" /> | |||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" /> | |||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | |||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | |||
<uses-permission android:name="android.permission.WRITE_SETTINGS" /> | |||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> | |||
<uses-permission android:name="android.permission.KOOLCLOUD_PRINTER" /> | |||
<uses-permission android:name="com.hidglobal.ia.omnikey.service.permission.SMARTCARDIO" /> | |||
<!-- 外置存储存取权限 --> | |||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> | |||
<uses-feature android:name="android.hardware.screen.portrait" /> | |||
<!-- 接收开机广播 --> | |||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | |||
<!-- 交易权限 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.advance.special" /> | |||
<!-- 收银 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.multiPurchase" /> | |||
<!-- 余额查询 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.inquery" /> | |||
<!-- 积分查询 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.integralQuery" /> | |||
<!-- 当日撤销 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.posVoid" /> | |||
<!-- 隔日退货 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.refund" /> | |||
<!-- 积分退货 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.integralRefund" /> | |||
<!-- 二维码退货 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.qrRefund" /> | |||
<!-- 预授权 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.preauth" /> | |||
<!-- 追加预授权 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.addPreauth" /> | |||
<!-- 预授权确认 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.preauthEnd" /> | |||
<!-- 预授权撤销 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.preauthVoid" /> | |||
<!-- 查本机交易记录 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.queryTransRec" /> | |||
<!-- 获取终端信息 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.terminalInfo" /> | |||
<!-- 重打印交易凭条 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.trans.reprint" /> | |||
<!-- 设备使用权限 --> | |||
<!-- 打印机 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.device.printer" /> | |||
<!-- 扫码器 --> | |||
<uses-permission android:name="com.icbc.smartpos.bankpay.device.scanner" /> | |||
<uses-sdk android:targetSdkVersion="23" /> | |||
<application | |||
android:name=".aplication.MyApplication" | |||
android:allowBackup="true" | |||
android:hardwareAccelerated="true" | |||
android:icon="@mipmap/ic_launcher" | |||
android:largeHeap="true" | |||
android:label="@string/app_name" | |||
android:supportsRtl="true" | |||
android:theme="@style/AppTheme"> | |||
<activity | |||
android:name=".LoginActivity" | |||
android:launchMode="singleTask" > | |||
<intent-filter> | |||
<action android:name="android.intent.action.MAIN" /> | |||
<category android:name="android.intent.category.LAUNCHER" /> | |||
<category android:name="android.intent.category.HOME" /> | |||
<category android:name="android.intent.category.DEFAULT" /> | |||
</intent-filter> | |||
</activity> | |||
<!-- 关机广播 --> | |||
<receiver android:name=".service.recevier.ShutDownRecevier"> | |||
<intent-filter> | |||
<action android:name="android.intent.action.ACTION_SHUTDOWN" /> | |||
<category android:name="android.intent.category.HOME" /> | |||
</intent-filter> | |||
</receiver> | |||
<!-- 开机广播 --> | |||
<receiver android:name=".service.recevier.BootRecevier"> | |||
<intent-filter> | |||
<action android:name="android.intent.action.BOOT_COMPLETED" /> | |||
<category android:name="android.intent.category.HOME" /> | |||
</intent-filter> | |||
</receiver> | |||
<receiver android:name=".service.recevier.UsbDeviceReceiver"> | |||
<intent-filter> | |||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> | |||
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" /> | |||
</intent-filter> | |||
</receiver> | |||
<activity | |||
android:name=".home.MainActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" /> | |||
<activity | |||
android:name=".home.MainActivityA90" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="portrait" /> | |||
<activity | |||
android:name=".cashfunction.activity.CashFunctionActivity" | |||
android:launchMode="singleTask" /> | |||
<activity | |||
android:name=".cashfunction.activity.CashMemberLoginActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:theme="@style/CustomActivityDialogStyle" /> | |||
<activity | |||
android:name=".cashfunction.activity.PayOrderActivity" | |||
android:launchMode="singleTask" | |||
android:theme="@style/CustomActivityDialogStyle" /> | |||
<activity | |||
android:name=".cashfunction.activity.CountInputActivity" | |||
android:launchMode="singleTask" | |||
android:theme="@style/CustomActivityDialogStyle" /> | |||
<activity | |||
android:name=".cashfunction.activity.MemberPayActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:theme="@style/CustomActivityDialogStyle" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".set.activity.SettingActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" /> | |||
<activity | |||
android:name=".set.activity.SettingActivityA90" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="portrait" /> | |||
<activity | |||
android:name=".set.activity.ShoppingCartModeActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape"/> | |||
<activity | |||
android:name=".paymanage.activity.TradeManageActivity" | |||
android:launchMode="singleTask" /> | |||
<activity | |||
android:name=".shop.activity.SelectShopActivity" | |||
android:launchMode="singleTask"/> | |||
<activity | |||
android:name=".member.activity.MemberLoginActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.MemberLoginSuccessActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.MemberRechargeActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.MemberRechargeChangeDetailsActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.MemberRegisterActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.MemberIntegralConsumeActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:theme="@style/CustomActivityDialogStyle" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.MemberMenuActivity" | |||
android:launchMode="singleTask" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.CardActivateActivity" | |||
android:launchMode="singleTask" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.CardRechargeActivity" | |||
android:launchMode="singleTask" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.CardRefundActivity" | |||
android:launchMode="singleTask" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".member.activity.CardOperateResultActivity" | |||
android:launchMode="singleTask" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<!-- 订单列表Activity --> | |||
<activity | |||
android:name=".order.activity.OrderListActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<!-- 订单Services --> | |||
<service android:name=".service.OrderService" /> | |||
<!-- 订单详情Activity --> | |||
<activity | |||
android:name=".order.activity.OrderDetailActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".paymanage.activity.RefundOrderActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" | |||
android:theme="@style/CustomActivityDialogStyle"/> | |||
<activity | |||
android:name=".paymanage.activity.OrderDetailInfoActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity | |||
android:name=".printer.PosSettingActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<activity android:name=".pay.recharge.RechargeActivity" /> | |||
<activity android:name=".set.activity.ModeChangeActivity" /> | |||
<!-- 后结选桌Activity --> | |||
<activity | |||
android:name=".aftersettlement.activity.ChooseTableActivity" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden" /> | |||
<!-- 后结菜单Activity --> | |||
<activity | |||
android:name=".aftersettlement.activity.MenuActivity" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="adjustPan" /> | |||
<activity | |||
android:name=".aftersettlement.activity.PaymentActivity" | |||
android:launchMode="singleTask" | |||
android:screenOrientation="landscape" | |||
android:windowSoftInputMode="stateHidden"/> | |||
<activity | |||
android:name=".paymanage.activity.PaymentDetailActivity" | |||
android:windowSoftInputMode="stateHidden"/> | |||
<activity | |||
android:name=".pay.BankCardRefundActivity" | |||
android:screenOrientation="landscape" | |||
android:theme="@android:style/Theme.NoDisplay" | |||
android:windowSoftInputMode="stateHidden"/> | |||
</application> | |||
</manifest> |
@@ -0,0 +1,3 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
parcelable CandidateAppInfo; |
@@ -0,0 +1,133 @@ | |||
/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import android.os.Parcel; | |||
import android.os.Parcelable; | |||
import android.os.Parcelable.Creator; | |||
public class CandidateAppInfo implements Parcelable { | |||
/**候选应用标识符(AID)*/ | |||
private byte[] tAID = new byte[16]; | |||
/**候选应用标签*/ | |||
private byte[] tAppLabel = new byte[16]; | |||
/**应用优先级指示符*/ | |||
private byte[] tAPN = new byte[16]; | |||
private byte cAPID; | |||
private byte cFlgAPID; | |||
/**语言参考*/ | |||
private byte[] sLangPref = new byte[8]; | |||
/**发行方证书索引*/ | |||
private byte cIssCTIndex; | |||
private byte cFlgIssCTIndex; | |||
public static final Creator<CandidateAppInfo> CREATOR = new Creator() { | |||
public CandidateAppInfo createFromParcel(Parcel source) { | |||
CandidateAppInfo appInfo = new CandidateAppInfo(); | |||
readByteArray(source.createByteArray(), appInfo.tAID); | |||
readByteArray(source.createByteArray(), appInfo.tAppLabel); | |||
readByteArray(source.createByteArray(), appInfo.tAPN); | |||
appInfo.cAPID = source.readByte(); | |||
appInfo.cFlgAPID = source.readByte(); | |||
readByteArray(source.createByteArray(), appInfo.sLangPref); | |||
appInfo.cIssCTIndex = source.readByte(); | |||
appInfo.cFlgIssCTIndex = source.readByte(); | |||
return appInfo; | |||
} | |||
public CandidateAppInfo[] newArray(int size) { | |||
return new CandidateAppInfo[size]; | |||
} | |||
private int getMinValue(int value1, int value2) { | |||
return ((value1 > value2) ? value2 : value1); | |||
} | |||
private void readByteArray(byte[] source, byte[] dest) { | |||
int arrayLen = 0; | |||
arrayLen = getMinValue(source.length, dest.length); | |||
System.arraycopy(source, 0, dest, 0, arrayLen); | |||
} | |||
}; | |||
public void setAID(byte[] aid) { | |||
this.tAID = aid; | |||
} | |||
public byte[] getAID() { | |||
return this.tAID; | |||
} | |||
public void setAppLabel(byte[] appLabel) { | |||
this.tAppLabel = appLabel; | |||
} | |||
public byte[] getAppLabel() { | |||
return this.tAppLabel; | |||
} | |||
public void setAPN(byte[] apn) { | |||
this.tAPN = apn; | |||
} | |||
public byte[] getAPN() { | |||
return this.tAPN; | |||
} | |||
public void setAPID(byte apid) { | |||
this.cAPID = apid; | |||
} | |||
public byte getAPID() { | |||
return this.cAPID; | |||
} | |||
public void setAPIDFlag(byte flag) { | |||
this.cFlgAPID = flag; | |||
} | |||
public byte getAPIDFlag() { | |||
return this.cFlgAPID; | |||
} | |||
public void setLangPref(byte[] langPref) { | |||
this.sLangPref = langPref; | |||
} | |||
public byte[] getLangPref() { | |||
return this.sLangPref; | |||
} | |||
public void setIssCTIndex(byte index) { | |||
this.cIssCTIndex = index; | |||
} | |||
public byte getIssCTIndex() { | |||
return this.cIssCTIndex; | |||
} | |||
public void setIssCTIndexFlag(byte flag) { | |||
this.cFlgIssCTIndex = flag; | |||
} | |||
public byte getIssCTIndexFlag() { | |||
return this.cFlgIssCTIndex; | |||
} | |||
public int describeContents() { | |||
return 0; | |||
} | |||
public void writeToParcel(Parcel dest, int flags) { | |||
dest.writeByteArray(this.tAID); | |||
dest.writeByteArray(this.tAppLabel); | |||
dest.writeByteArray(this.tAPN); | |||
dest.writeByte(this.cAPID); | |||
dest.writeByte(this.cFlgAPID); | |||
dest.writeByteArray(this.sLangPref); | |||
dest.writeByte(this.cIssCTIndex); | |||
dest.writeByte(this.cFlgIssCTIndex); | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 检卡过程监听接口定义 | |||
* @author: baoxl | |||
*/ | |||
interface CheckCardListener { | |||
/** | |||
* 检测到磁条卡 | |||
* @param track - 磁卡数据 | |||
* <ul> | |||
* <li>PAN(String) - 主账号(卡号)</li> | |||
* <li>TRACK1(String) - 磁道1数据</li> | |||
* <li>TRACK2(String) - 磁道2数据 </li> | |||
* <li>TRACK3(String) - 磁道3数据</li> | |||
* <li>SERVICE_CODE(String) - 服务码 </li> | |||
* <li>EXPIRED_DATE(String) - 卡片有效期 </li> | |||
* </ul> | |||
*/ | |||
void onCardSwiped(in Bundle track); | |||
/** | |||
* IC卡上电成功 | |||
*/ | |||
void onCardPowerUp(); | |||
/** | |||
* 非接卡激活成功 | |||
*/ | |||
void onCardActivate(); | |||
/** | |||
* 检卡超时 | |||
*/ | |||
void onTimeout(); | |||
/** | |||
* 检卡出错 | |||
* @param error - 错误码 | |||
* <ul> | |||
* <li>SERVICE_CRASH(99) - 服务崩溃 </li> | |||
* <li>REQUEST_EXCEPTION(100) - 请求异常</li> | |||
* <li>MAG_SWIPE_ERROR(1) - 刷卡失败</li> | |||
* <li>IC_INSERT_ERROR(2) - 插卡失败</li> | |||
* <li>IC_POWERUP_ERROR(3) - 卡上电失败</li> | |||
* <li>RF_PASS_ERROR(4) - 非接挥卡失败</li> | |||
* <li>RF_ACTIVATE_ERROR(5) - 非接卡激活失败</li> | |||
* </ul> | |||
* @param message - 错误描述 | |||
*/ | |||
void onError(int error, String message); | |||
} |
@@ -0,0 +1,14 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
public class FontFamily { | |||
public static final int SMALL = 0; | |||
public static final int MIDDLE = 1; | |||
public static final int BIG = 2; | |||
public static final boolean NEED_BOLD = true; | |||
public static final boolean NOT_NEED_BOLD = false; | |||
public static final int LEFT = 0; | |||
public static final int CENTER = 1; | |||
public static final int RIGHT = 2; | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 蜂鸣器对象 | |||
* @author: baoxl | |||
*/ | |||
interface IBeeper { | |||
/** | |||
* 开始蜂鸣<br/> | |||
* 通过调用该方法可以让POS的蜂鸣器持续鸣叫msec毫秒。如果用户设定msec为0则不进行鸣叫。 | |||
* 蜂鸣器发声是采用非阻塞方式进行的,调用该函数之后,程序立即退出,不会停留在该函数。 | |||
* @param msec - 需要鸣叫的时间,单位ms | |||
*/ | |||
void startBeep(int msec); | |||
/** | |||
* 停止蜂鸣<br/> | |||
* 调用该方法后立即停止鸣叫 | |||
*/ | |||
void stopBeep(); | |||
} |
@@ -0,0 +1,102 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import com.icbc.smartpos.deviceservice.aidl.TusnData; | |||
/** | |||
* 终端设备信息对象 | |||
* @author: baoxl | |||
*/ | |||
interface IDeviceInfo { | |||
/** | |||
* 获取终端序列号 | |||
* @return 终端设备序列号 | |||
*/ | |||
String getSerialNo(); | |||
/** | |||
* 获取终端IMSI号 | |||
* @return 终端IMSI号 | |||
*/ | |||
String getIMSI(); | |||
/** | |||
* 获取终端IMEI号 | |||
* @return 终端IMEI号 | |||
*/ | |||
String getIMEI(); | |||
/** | |||
* 获取SIM卡ICCID | |||
* @return 终端SIM卡ICCID | |||
*/ | |||
String getICCID(); | |||
/** | |||
* 获取厂商名称 | |||
* @return 终端厂商名称 | |||
*/ | |||
String getManufacture(); | |||
/** | |||
* 获取终端设备型号 | |||
* @return 终端设备型号 | |||
*/ | |||
String getModel(); | |||
/** | |||
* 获取终端Android操作系统版本 | |||
* @return 终端Android系统版本 | |||
*/ | |||
String getAndroidOSVersion(); | |||
/** | |||
* 获取终端Android内核版本 | |||
* @return 终端Android内核版本 | |||
*/ | |||
String getAndroidKernelVersion(); | |||
/** | |||
* 获取终端ROM版本 | |||
* @return 终端系统ROM版本 | |||
*/ | |||
String getROMVersion(); | |||
/** | |||
* 获取终端固件版本 | |||
* @return 终端固件版本 | |||
*/ | |||
String getFirmwareVersion(); | |||
/** | |||
* 获取终端硬件版本 | |||
* @return 终端硬件版本 -后标贴的硬件版本号,如H:L4200151361,测试机器可能没有录入 | |||
*/ | |||
String getHardwareVersion(); | |||
/** | |||
* 更新终端系统时间 | |||
* @param date - 日期,格式yyyyMMdd | |||
* @param time - 时间,格式HHmmss | |||
* @return 更新成功返回true,失败返回false | |||
**/ | |||
boolean updateSystemTime(String date, String time); | |||
/** | |||
* 设置系统功能(设置全局有效) | |||
* @param bundle - 设置参数 | |||
* <ul> | |||
* <li>HOMEKEY(boolean) – 是否允许使用HOME键,true可用,false禁用(注:在机器重启后会取消屏蔽)</li> | |||
* <li>STATUSBARKEY(boolean) – 是否允许使用下拉菜单,true可用,false禁用</li> | |||
* </ul> | |||
* @return 设置成功返回true,失败返回false | |||
**/ | |||
boolean setSystemFunction(in Bundle bundle); | |||
/** | |||
* 获取银联终端唯一终端号TUSN | |||
* @param mode 模式, 预留参数, 需为0 | |||
* @param input 指 对TUSN计算 MAC 时,参与计算的随机数(随机因子),允许范围:4~10字节 | |||
* @return 成功返回TUSN数据,失败返回null。 | |||
**/ | |||
TusnData getTUSN(int mode, in byte[] input); | |||
} |
@@ -0,0 +1,88 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import com.icbc.smartpos.deviceservice.aidl.IBeeper; | |||
import com.icbc.smartpos.deviceservice.aidl.IDeviceInfo; | |||
import com.icbc.smartpos.deviceservice.aidl.ILed; | |||
import com.icbc.smartpos.deviceservice.aidl.IMagCardReader; | |||
import com.icbc.smartpos.deviceservice.aidl.IDeviceService; | |||
import com.icbc.smartpos.deviceservice.aidl.IInsertCardReader; | |||
import com.icbc.smartpos.deviceservice.aidl.IPBOC; | |||
import com.icbc.smartpos.deviceservice.aidl.IPinpad; | |||
import com.icbc.smartpos.deviceservice.aidl.IPrinter; | |||
import com.icbc.smartpos.deviceservice.aidl.IRFCardReader; | |||
import com.icbc.smartpos.deviceservice.aidl.IScanner; | |||
import com.icbc.smartpos.deviceservice.aidl.ISerialPort; | |||
/** | |||
* 设备服务对象,提供范围终端各外设对象的服务接口 | |||
* @author: baoxl | |||
*/ | |||
interface IDeviceService { | |||
/** | |||
* 获取蜂鸣器操作对象 | |||
* @return IBeeper对象,参见IBeeper.aidl类 | |||
*/ | |||
IBeeper getBeeper(); | |||
/** | |||
* 获取LED灯操作对象 | |||
* @return ILed对象,参见ILed.aidl类 | |||
*/ | |||
ILed getLed(); | |||
/** | |||
* 获取串口操作对象 | |||
* @return ISerialPort对象,参见ISerialPort.aidl类 | |||
*/ | |||
ISerialPort getSerialPort(); | |||
/** | |||
* 获取扫码器操作对象 | |||
* @param cameraId - 0前置扫码器,1后置扫码器 | |||
* @return IScanner对象,参见IScanner.aidl类 | |||
*/ | |||
IScanner getScanner(int cameraId); | |||
/** | |||
* 获取磁卡操作句柄 | |||
* @return IMagCardReader对象,参见IMagCardReader.aidl类 | |||
*/ | |||
IMagCardReader getMagCardReader(); | |||
/** | |||
* 获取IC卡操作对象 | |||
* @return IC卡操作对象,参见IInsertCardReader.aidl类 | |||
*/ | |||
IInsertCardReader getInsertCardReader(); | |||
/** | |||
* 获取RF卡操作对象 | |||
* @return RF卡操作对象,参见IRFCardReader.aidl类 | |||
*/ | |||
IRFCardReader getRFCardReader(); | |||
/** | |||
* 获取密码键盘操作对象 | |||
* @param kapId 密码键盘kapId索引,每个kapId对一个逻辑密码键盘 | |||
* @return IPinpad对象,参见IPinpad.aidl类 | |||
*/ | |||
IPinpad getPinpad(int kapId); | |||
/** | |||
* 获取打印机操作对象 | |||
* @return IPrinter对象,参见IPrinter.aidl类 | |||
*/ | |||
IPrinter getPrinter(); | |||
/** | |||
* 获取PBOC(EMV)流程操作对象 | |||
* @return IPBOC对象,参见IPBOC.aidl类 | |||
*/ | |||
IPBOC getPBOC(); | |||
/** | |||
* 设备操作对象 | |||
* @return IDeviceInfo对象,参见IDeviceInfo.aidl类 | |||
*/ | |||
IDeviceInfo getDeviceInfo(); | |||
} |
@@ -0,0 +1,43 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 接触式IC读卡器对象 | |||
* @author: baoxl | |||
*/ | |||
interface IInsertCardReader { | |||
/** | |||
* 卡上电 | |||
* @return 上电成功true,上电失败false。 | |||
*/ | |||
boolean powerUp(); | |||
/** | |||
* 卡下电 | |||
* @return 关闭成功true,失败false。 | |||
*/ | |||
boolean powerDown(); | |||
/** | |||
* 卡片是否在位 | |||
* @return 卡在位true,卡不在位false。 | |||
*/ | |||
boolean isCardIn(); | |||
/** | |||
* APDU数据通讯 | |||
* @param apdu - apdu数据 | |||
* @return 成功返回卡片应答数据,失败返回null。 | |||
*/ | |||
byte[] exchangeApdu(in byte[] apdu); | |||
/** | |||
* 卡片复位 | |||
* @param resetType - 复位类型 | |||
* <ul> | |||
* <li>0- WARMRESET</li> | |||
* <li>1- COLDRESET</li> | |||
* </ul> | |||
* @return 卡片复位应答数据。 | |||
*/ | |||
byte[] cardReset(int resetType); | |||
} |
@@ -0,0 +1,31 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 设备LED灯对象 | |||
* @author: baoxl | |||
*/ | |||
interface ILed { | |||
/** | |||
* 点亮LED灯 | |||
* @param led - 灯标识 | |||
* <ul> | |||
* <li> 1 - 蓝灯</li> | |||
* <li> 2 - 黄灯</li> | |||
* <li> 3 - 绿灯</li> | |||
* <li> 4 - 红灯</li> | |||
* </ul> | |||
*/ | |||
void turnOn(int led); | |||
/** | |||
* 关闭LED灯 | |||
* @param led - 灯标识 | |||
* <ul> | |||
* <li> 1 - 蓝灯</li> | |||
* <li> 2 - 黄灯</li> | |||
* <li> 3 - 绿灯</li> | |||
* <li> 4 - 红灯</li> | |||
* </ul> | |||
*/ | |||
void turnOff(int led); | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import com.icbc.smartpos.deviceservice.aidl.MagCardListener; | |||
/** | |||
* 磁条卡刷卡器对象 | |||
* @author: baoxl | |||
*/ | |||
interface IMagCardReader { | |||
/** | |||
* 等待刷卡,获取磁卡卡片的磁道数据 | |||
* @param timeout - 检卡超时时间(单位秒) | |||
* @param listener - 磁卡刷卡监听器 | |||
*/ | |||
void searchCard(int timeout, MagCardListener listener); | |||
/** | |||
* 取消等待刷卡 | |||
*/ | |||
void stopSearch(); | |||
} |
@@ -0,0 +1,213 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import com.icbc.smartpos.deviceservice.aidl.CheckCardListener; | |||
import com.icbc.smartpos.deviceservice.aidl.UPCardListener; | |||
import com.icbc.smartpos.deviceservice.aidl.PBOCHandler; | |||
import com.icbc.smartpos.deviceservice.aidl.OnlineResultHandler; | |||
import com.icbc.smartpos.deviceservice.aidl.CandidateAppInfo; | |||
/** | |||
* PBOC金融交易对象<br/> | |||
* 用来处理金融卡相关的EMV交易流程。 | |||
* @author: baoxl | |||
*/ | |||
interface IPBOC { | |||
/** | |||
* PBOC检卡 | |||
* @param cardOption - 支持的卡类型,磁条卡、IC卡和非接卡 | |||
* <ul> | |||
* <li>supportMagCard(boolean) - 是否支持磁条卡</li> | |||
* <li>supportICCard(boolean) - 是否支持IC卡</li> | |||
* <li>supportRFCard(boolean) - 是否支持非接卡</li> | |||
* </ul> | |||
* @param timeout - 超时时间 | |||
* @param listener - 检卡结果监听器 | |||
*/ | |||
void checkCard(in Bundle cardOption, int timeout, CheckCardListener listener); | |||
/** | |||
* 停止PBOC检卡 | |||
*/ | |||
void stopCheckCard(); | |||
/** | |||
* 读取手机芯片卡 | |||
* @param listener - 读卡结果监听器 | |||
*/ | |||
void readUPCard(UPCardListener listener); | |||
/** | |||
* 启动PBOC处理流程 | |||
* @param transType - 交易类型 | |||
* <ul> | |||
* <li>EC_BALANCE(1) - 查询电子现金余额</li> | |||
* <li>TRANSFER(2) - 转账</li> | |||
* <li>EC_LOAD(3) - 指定账户圈存</li> | |||
* <li>EC_LOAD_U(4) - 非制定账户圈存</li> | |||
* <li>EC_LOAD_CASH(5) - 现金圈存/现金充值</li> | |||
* <li>EC_LOAD_CASH_VOID(6) - 圈存撤销</li> | |||
* <li>PURCHASE(7) - 消费</li> | |||
* <li>Q_PURCHASE(8) - 快速支付</li> | |||
* <li>CHECK_BALANCE(9) - 余额查询</li> | |||
* <li>PRE_AUTH(10) - 预授权交易</li> | |||
* <li>SALE_VOID(11) - 消费撤销</li> | |||
* <li>SIMPLE_PROCESS(12) - 简易流程交易</li> | |||
* <li>Q_QUERY(13) - qPBOC查询</li> | |||
* </ul> | |||
* @param intent - 请求数据 | |||
* <ul> | |||
* <li>cardType(int):卡类型,CARD_INSERT(0)-接触式IC卡,CARD_RF(1)-非接触式卡</li> | |||
* <li>authAmount(long):授权金额(交易金额)</li> | |||
* <li>isSupportQ(boolean):是否支持QPBOC</li> | |||
* <li>isSupportSM(boolean):是否支持国密</li> | |||
* <li>isQPBOCForceOnline(boolean):QPBOC是否支持强制联机</li> | |||
* <li>merchantName(String):商户名称</li> | |||
* <li>merchantId(String):商户号</li> | |||
* <li>terminalId(String):终端号</li> | |||
* </ul> | |||
* @param handler - PBOC流程回调处理器 | |||
*/ | |||
void startPBOC(int transType, in Bundle intent, PBOCHandler handler); | |||
/** | |||
* 终止PBOC流程 | |||
*/ | |||
void abortPBOC(); | |||
/** | |||
* 更新AID参数 | |||
* @param operation - 操作标识,增、删、清空 | |||
* <ul> | |||
* <li>1:新增 </li> | |||
* <li>2:删除 </li> | |||
* <li>3:清空 </li> | |||
* </ul> | |||
* @param aidType - AID参数类型 | |||
* <ul> | |||
* <li>1:接触式 </li> | |||
* <li>2:非接</li> | |||
* </ul> | |||
* @param aid - AID参数,构成AID参数的TLV字符串 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean updateAID(int operation,int aidType, String aid); | |||
/** | |||
* 获取当前aid参数TLV数据列表,在选择完应用后可以调用 | |||
* @param taglist - 需要获得的tag列表 | |||
* @return tlv数据,null表示没有数据 | |||
*/ | |||
String getAidParams(in String[] taglist); | |||
/** | |||
* 更新CA公钥参数 | |||
* @param operation - 操作标识,增、删、清空 | |||
* <ul> | |||
* <li>1:新增 </li> | |||
* <li>2:删除 </li> | |||
* <li>3:清空 </li> | |||
* </ul> | |||
* @param rid - CA公钥参数 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean updateRID(int operation, String rid); | |||
/** | |||
* 导入金额 | |||
* @param amount - 交易金额 | |||
*/ | |||
void importAmount(long amount); | |||
/** | |||
* 导入应用选择结果(多应用卡片) | |||
* @param index - 应用选择的序号从1开始,0为取消 | |||
*/ | |||
void importAppSelect(int index); | |||
/** | |||
* 导入PIN | |||
* @param option(int) - 操作选项 | |||
* <ul> | |||
* <li> CANCEL(0) - 取消 </li> | |||
* <li> CONFIRM(1) - 确认 </li> | |||
* </ul> | |||
* @param pin - PIN数据 | |||
*/ | |||
void importPin(int option, in byte[] pin); | |||
/** | |||
* 导入身份认证结果 | |||
* @param option - 确认结果 | |||
* <ul> | |||
* <li> CANCEL(0) - 取消,跳过(BYPASS) </li> | |||
* <li> CONFIRM(1) - 确认持卡人身份 </li> | |||
* <li> NOTMATCH(2) - 身份信息不符 </li> | |||
* </ul> | |||
*/ | |||
void importCertConfirmResult(int option); | |||
/** | |||
* 导入卡信息确认结果 | |||
* @param pass - 是否通过 | |||
*/ | |||
void importCardConfirmResult(boolean pass); | |||
/** | |||
* 导入联机应答数据 | |||
* @param onlineResult - 联机结果 | |||
* <ul> | |||
* <li> isOnline(boolean) - 是否联机</li> | |||
* <li> respCode(String) - 应答码</li> | |||
* <li> authCode(String) - 授权码 </li> | |||
* <li> field55(String) - 55域应答数据</li> | |||
* </ul> | |||
* @param handler - 联机结果处理 | |||
*/ | |||
void inputOnlineResult(in Bundle onlineResult, OnlineResultHandler handler); | |||
/** | |||
* 设置TLV内核参数 | |||
* @param tlvList 内置参数TLV列表 | |||
*/ | |||
void setEMVData(in List<String> tlvList); | |||
/** | |||
* 获取PBOC内核TLV数据列表 | |||
* @param taglist - 需要获得的tag列表 | |||
* @return tlv数据,null表示没有数据 | |||
*/ | |||
String getAppTLVList(in String[] taglist); | |||
/** | |||
* 获取卡片上EMV数据元 | |||
* @param tagName - 标签名称 | |||
* @return 卡片EMV数据 | |||
*/ | |||
byte[] getCardData(String tagName); | |||
/** | |||
* 获取PBOC流程数据,如卡号、有效期、卡片序列号等。<br/> | |||
* <em>EMV必须执行到相应流程才能获取到对应的卡片数据,否则返回空。</em> | |||
* @param tagName - 标签名称 | |||
* <ul> | |||
* <li> PAN - 卡号</li> | |||
* <li> TRACK2 - 磁道二数据</li> | |||
* <li> CARD_SN - 卡片序列号 </li> | |||
* <li> EXPIRED_DATE - 有效期</li> | |||
* <li> DATE - 日期</li> | |||
* <li> TIME - 时间</li> | |||
* <li> BALANCE - 电子现金余额</li> | |||
* <li> CURRENCY - 币种</li> | |||
* </ul> | |||
* @return PBOC流程中内核返回的数据 | |||
*/ | |||
String getPBOCData(String tagName); | |||
/** | |||
* 获取卡片候选应用信息<br/> | |||
* 用于电子签名上送和小额免签免密。 | |||
* @return 卡片候选应用信息,具体见{@link CandidateAppInfo}定义 | |||
*/ | |||
CandidateAppInfo getCandidateAppInfo(); | |||
} |
@@ -0,0 +1,113 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import com.icbc.smartpos.deviceservice.aidl.PinInputListener; | |||
/** | |||
* 密码键盘对象<br/> | |||
* 实现金融交易过程中密钥管理、数据加密和PIN输入处理过程。 | |||
* @author: baoxl | |||
*/ | |||
interface IPinpad { | |||
/** | |||
* 判断密钥是否存在(密钥已下载)<br/> | |||
* @param keyType - 密钥类型 | |||
* <ul> | |||
* <li>0-主密钥</li> | |||
* <li>1-MAC密钥</li> | |||
* <li>2-PIN密钥</li> | |||
* <li>3-TD密钥</li> | |||
* </ul> | |||
* @param keyId - 密钥ID | |||
* @return 存在返回true,不存在返回false | |||
**/ | |||
boolean isKeyExist(int keyType, int keyId); | |||
/** | |||
* 下装TEK密钥<br/> | |||
* TEK是加密主密钥的密钥KEK | |||
* @param keyId - 密钥存储的ID | |||
* @param key - 密钥 | |||
* @param checkValue - 校验值 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean loadTEK(int keyId, in byte[] key, in byte[] checkValue); | |||
/** | |||
* 下装密文主密钥<br/> | |||
* @param keyId - 下装密钥存储ID | |||
* @param key - 密钥 | |||
* @param checkValue - 校验值 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean loadEncryptMainKey(int keyId, in byte[] key, in byte[] checkValue); | |||
/** | |||
* 下装明文主密钥<br/> | |||
* @param keyId - 下装密钥存储ID | |||
* @param key - 密钥 | |||
* @param checkValue - 校验值 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean loadMainKey(int keyId, in byte[] key, in byte[] checkValue); | |||
/** | |||
* 下装工作密钥<br/> | |||
* @param keyType - 下装工作密钥类型,1-MAC密钥,2-PIN密钥,3-TD密钥 | |||
* @param mkId - 解密工作密钥的主密钥ID | |||
* @param keyId - 下装密钥存储ID | |||
* @param key - 密钥 | |||
* @param checkValue - 校验值 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean loadWorkKey(int keyType, int mkId, int wkId, in byte[] key, in byte[] checkValue); | |||
/** | |||
* 计算MAC值<br/> | |||
* @param keyId - MAC密钥索引 | |||
* @param data - 计算MAC的数据 | |||
* @return 成功返回mac值,失败返回null | |||
*/ | |||
byte[] calcMAC(int keyId, in byte[] data); | |||
/** | |||
* 加密磁道数据<br/> | |||
* @param mode - 加密模式 | |||
* @param keyId - TDK密钥索引 | |||
* @param trkData - 待加密磁道数据 | |||
* @return 成功返回加密后的磁道数据,失败返回null | |||
*/ | |||
byte[] encryptTrackData(int mode, int keyId, in byte[] trkData); | |||
/** | |||
* 启动PIN输入<br/> | |||
* @param keyId - PIN密钥索引 | |||
* @param param - PIN输入配置参数 | |||
* <ul> | |||
* <li>pinLimit(byte[]) - 允许输入密码的长度</li> | |||
* <li>timeout(int) - 输入超时时间,单位(秒)</li> | |||
* <li>isOnline(boolean) - 是否联机PIN</li> | |||
* <li>promptString(String) - 提示信息</li> | |||
* <li>pan(String) - 用于加密联机PIN的主帐号(卡号)</li> | |||
* </ul> | |||
* @param listener - PIN输入过程监听器 | |||
* @return | |||
*/ | |||
void startPinInput(int keyId, in Bundle param, PinInputListener listener); | |||
/** | |||
* 提交PIN输入 | |||
*/ | |||
void submitPinInput(); | |||
/** | |||
* 取消PIN输入过程 | |||
*/ | |||
void stopPinInput(); | |||
/** | |||
* 获取最近一次操作的错误<br/> | |||
* @return 错误描述 | |||
*/ | |||
String getLastError(); | |||
} |
@@ -0,0 +1,105 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import com.icbc.smartpos.deviceservice.aidl.PrinterListener; | |||
/** | |||
* 打印机对象<br/> | |||
* 实现对文本、条形码、二维码和图片的打印功能。 | |||
* @author: baoxl | |||
*/ | |||
interface IPrinter { | |||
/** | |||
* 获取打印机状态 | |||
* @return 打印机状态 | |||
* <ul> | |||
* <li>ERROR_NONE(0x00) - 状态正常</li> | |||
* <li>ERROR_PAPERENDED(0xF0) - 缺纸,不能打印</li> | |||
* <li>ERROR_HARDERR(0xF2) - 硬件错误</li> | |||
* <li>ERROR_OVERHEAT(0xF3) - 打印头过热</li> | |||
* <li>ERROR_BUFOVERFLOW(0xF5) - 缓冲模式下所操作的位置超出范围 </li> | |||
* <li>ERROR_LOWVOL(0xE1) - 低压保护 </li> | |||
* <li>ERROR_PAPERENDING(0xF4) - 纸张将要用尽,还允许打印(单步进针打特有返回值)</li> | |||
* <li>ERROR_MOTORERR(0xFB) - 打印机芯故障(过快或者过慢)</li> | |||
* <li>ERROR_PENOFOUND(0xFC) - 自动定位没有找到对齐位置,纸张回到原来位置 </li> | |||
* <li>ERROR_PAPERJAM(0xEE) - 卡纸</li> | |||
* <li>ERROR_NOBM(0xF6) - 没有找到黑标</li> | |||
* <li>ERROR_BUSY(0xF7) - 打印机处于忙状态</li> | |||
* <li>ERROR_BMBLACK(0xF8) - 黑标探测器检测到黑色信号</li> | |||
* <li>ERROR_WORKON(0xE6) - 打印机电源处于打开状态</li> | |||
* <li>ERROR_LIFTHEAD(0xE0) - 打印头抬起(自助热敏打印机特有返回值)</li> | |||
* <li>ERROR_CUTPOSITIONERR(0xE2) - 切纸刀不在原位(自助热敏打印机特有返回值)</li> | |||
* <li>ERROR_LOWTEMP(0xE3) - 低温保护或AD出错(自助热敏打印机特有返回值)</li> | |||
* </ul> | |||
*/ | |||
int getStatus(); | |||
/** | |||
* 设置打印灰度 | |||
* @param gray - 打印灰度0~10 | |||
*/ | |||
void setGray(int gray); | |||
/** | |||
* 添加一行打印文本 | |||
* @param format - 打印字体格式 | |||
* <ul> | |||
* <li>font(int) - 0:small, 1:normal, 2:large</li> | |||
* <li>align(int) - 0:left, 1:center, 2:right</li> | |||
* </ul> | |||
* @param text - 打印文本 | |||
*/ | |||
void addText(in Bundle format, String text); | |||
/** | |||
* 添加条码打印 | |||
* @param format - 打印格式,可设置打印的位置、宽度、高度 | |||
* <ul> | |||
* <li>align(int) - 0:left, 1:center, 2:right</li> | |||
* <li>width(int) - 宽度</li> | |||
* <li>height(int) - 高度</li> | |||
* </ul> | |||
* @param barcode - 条码内容 | |||
*/ | |||
void addBarCode(in Bundle format, in String barcode); | |||
/** | |||
* 添加二维码打印 | |||
* @param format - 打印格式,可设置打印的位置、期望高度 | |||
* <ul> | |||
* <li>offset(int) - 打印起始位置 </li> | |||
* <li>expectedHeight(int) - 期望高度</li> | |||
* </ul> | |||
* @param qrCode - 二维码内容 | |||
*/ | |||
void addQrCode(in Bundle format, String qrCode); | |||
/** | |||
* 添加图片打印 | |||
* @param format - 打印格式,可设置打印的位置、宽度、高度 | |||
* <ul> | |||
* <li>offset(int) - 打印起始位置</li> | |||
* <li>width(int) - 宽度</li> | |||
* <li>height(int) - 高度</li> | |||
* </ul> | |||
* @param imageData - 图片数据 | |||
*/ | |||
void addImage(in Bundle format, in byte[] imageData); | |||
/** | |||
* 走纸 | |||
* @param lines - 行数 | |||
*/ | |||
void feedLine(int lines); | |||
/** | |||
* 启动打印 | |||
* @param listener - 打印结果监听器 | |||
*/ | |||
void startPrint(PrinterListener listener); | |||
/** | |||
* 启动打印(打印不清缓存) | |||
* @param listener - 打印结果监听器 | |||
*/ | |||
void startSaveCachePrint(PrinterListener listener); | |||
} |
@@ -0,0 +1,127 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import com.icbc.smartpos.deviceservice.aidl.RFSearchListener; | |||
/** | |||
* 非接触式IC读卡器对象 | |||
* @author: baoxl | |||
*/ | |||
interface IRFCardReader { | |||
/** | |||
* 非接寻卡上电<br/> | |||
* @param listener - 寻卡监听器 | |||
*/ | |||
void searchCard(RFSearchListener listener); | |||
/** | |||
* 停止寻卡操作 | |||
* @return 关闭成功true,失败false。 | |||
*/ | |||
void stopSearch(); | |||
/** | |||
* 激活卡片<br/> | |||
* @param driver - 驱动名称 | |||
* <ul> | |||
* <li>"S50" - S50卡</li> | |||
* <li>"S70" - S70卡</li> | |||
* <li>"CPU" - CPU卡</li> | |||
* <li>"PRO" - PRO、S5O_PRO、S70_PRO卡</li> | |||
* </ul> | |||
* @param responseData - 激活卡片应答数据 | |||
* @return 返回0表示激活成功,其他失败。 | |||
*/ | |||
int activate(String driver, out byte[] responseData); | |||
/** | |||
* 关闭设备,再次操作需从寻卡开始<br/> | |||
*/ | |||
void halt(); | |||
/** | |||
* 卡片是否在位 | |||
* @return 卡在位true,卡不在位false。 | |||
*/ | |||
boolean isExist(); | |||
/** | |||
* APDU数据通讯 | |||
* @param apdu - apdu数据 | |||
* @return 成功返回卡片应答数据。 | |||
*/ | |||
byte[] exchangeApdu(in byte[] apdu); | |||
/** | |||
* 卡片复位 | |||
* @param resetType - 复位类型 | |||
* <ul> | |||
* <li>0- WARMRESET</li> | |||
* <li>1- COLDRESET</li> | |||
* </ul> | |||
* @return 卡片复位应答数据。 | |||
*/ | |||
byte[] cardReset(int resetType); | |||
/** | |||
* 认证(根据块号) | |||
* <ul> | |||
* <li>对卡执行读写,修改的操作时必须先进行认证,而且同时只能认证一个扇区,在对不同的扇区进行操作时需要再做认证。</li><br/> | |||
* <li>该接口需要指定认证块的具体位置</li> | |||
* </ul> | |||
* @param blockNo 块号,从0开始 | |||
* @param keyType 密钥类型 KEY_A 或者 KEY_B | |||
* @param key 密钥,6字节长 | |||
* @return 成功0x00,其他失败 | |||
*/ | |||
int authBlock(int blockNo, int keyType, in byte[] key); | |||
/** | |||
* 认证(根扇区号)<br/> | |||
* <ul> | |||
* <li>对卡执行读写,修改的操作时必须先进行认证,而且同时只能认证一个扇区,在对不同的扇区进行操作时需要再做认证。</li><br/> | |||
* <li>该接口只需要指定要操作的扇区,会自动对该扇区的第3块(从0起)进行认证操作</li> | |||
* </ul> | |||
* @param sectorNo 扇区号,从0开始 | |||
* @param keyType 密钥类型 KEY_A 或者 KEY_B | |||
* @param key 密钥,6字节长 | |||
* @return 成功0x00,其他失败 | |||
*/ | |||
int authSector(int sectorNo, int keyType, in byte[] key); | |||
/** | |||
* 读块数据 | |||
* @param blockNo - 块号 | |||
* @param data - 读取返回的块数据(16字节数据) | |||
* @return 成功0x00,其他失败 | |||
*/ | |||
int readBlock(int blockNo, out byte[] data); | |||
/** | |||
* 写块数据<br/> | |||
* <ul> | |||
* <li>必须是16个字节的数据,没有达到字节数将认为是错误。</li> | |||
* </ul> | |||
* @param blockNo - 块号 | |||
* @param data - 指定数据写到指定的块里 | |||
* @return 成功0x00,其他失败 | |||
*/ | |||
int writeBlock(int blockNo, in byte[] data); | |||
/** | |||
* 加值</br> | |||
* 把指定的值加至一数值块中<br/> | |||
* @param blockNo - 块号 | |||
* @param value - 数据 | |||
* @return 成功0x00,其他失败 | |||
*/ | |||
int increaseValue(int blockNo, int value); | |||
/** | |||
* 减值</br> | |||
* 从一数值块中减去指定的值<br/> | |||
* @param blockNo - 块号 | |||
* @param value - 数据 | |||
* @return 成功0x00,其他失败 | |||
*/ | |||
int decreaseValue(int blockNo, int value); | |||
} |
@@ -0,0 +1,25 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import com.icbc.smartpos.deviceservice.aidl.ScannerListener; | |||
/** | |||
* 扫码器对象<br/> | |||
* 实现设备对二维码扫码功能。 | |||
* @author: baoxl | |||
*/ | |||
interface IScanner { | |||
/** | |||
* 启动扫码 | |||
* @param配置参数 | |||
* upPromptString(String):扫描框上方提示信息,最大20汉字,默认中间对齐, | |||
* downPromptString(String):扫描框下方提示信息,最大20汉字,默认中间对齐 | |||
* timeout - 超时时间,单位ms | |||
* listener - 扫码结果监听 | |||
*/ | |||
void startScan(in Bundle param, long timeout, ScannerListener listener); | |||
/** | |||
* 停止扫码 | |||
*/ | |||
void stopScan(); | |||
} |
@@ -0,0 +1,75 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 串口设备对象<br/> | |||
* 实现串口数据收发功能。 | |||
* @author: baoxl | |||
*/ | |||
interface ISerialPort { | |||
/** | |||
* 打开串口 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean open(); | |||
/** | |||
* 关闭串口 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean close(); | |||
/** | |||
* 初始化串口 | |||
* @param bps 波特率 | |||
* <ul> | |||
* <li>1200 - 对应1200波特率</li> | |||
* <li>2400 - 对应2400波特率</li> | |||
* <li>4800 - 对应4800波特率</li> | |||
* <li>9600 - 对应9600波特率</li> | |||
* <li>14400 - 对应14400波特率</li> | |||
* <li>19200 - 对应19200波特率</li> | |||
* <li>28800 - 对应28800波特率</li> | |||
* <li>38400 - 对应38400波特率</li> | |||
* <li>57600 - 对应57600波特率</li> | |||
* <li>115200 - 对应115200波特率</li> | |||
* </ul> | |||
* @param par 效验 | |||
* <ul> | |||
* <li>0 - 不校验</li> | |||
* <li>1 - 奇校验</li> | |||
* <li>2 - 偶校验</li> | |||
* </ul> | |||
* @param dbs 数据位 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean init(int bps, int par, int dbs); | |||
/** | |||
* 读数据(接收) | |||
* @param buffer 缓冲区 | |||
* @param timeout 超时时间,毫秒 | |||
* @return 返回实际读取的数据长度,失败返回-1 | |||
*/ | |||
int read(inout byte[] buffer, int timeout); | |||
/** | |||
* 写数据(发送) | |||
* @param data 要发送的数据 | |||
* @param timeout 超时时间,毫秒 | |||
* @return 返回实际写入的数据长度,失败返回-1 | |||
*/ | |||
int write(in byte[] data, int timeout); | |||
/** | |||
* 清除接收缓冲区 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean clearInputBuffer(); | |||
/** | |||
* 查看缓冲区是否为空 | |||
* @param input true为输入缓冲区,false为输出缓冲区 | |||
* @return 成功true,失败false | |||
*/ | |||
boolean isBufferEmpty(boolean input); | |||
} |
@@ -0,0 +1,38 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 磁条卡刷卡结果回调 | |||
* @author: baoxl | |||
*/ | |||
interface MagCardListener { | |||
/** | |||
* 刷卡成功回调 | |||
* @param track - 磁卡数据对象 | |||
* <ul> | |||
* <li>PAN(String) - 主账号(卡号)</li> | |||
* <li>TRACK1(String) - 磁道1数据</li> | |||
* <li>TRACK2(String) - 磁道2数据 </li> | |||
* <li>TRACK3(String) - 磁道3数据</li> | |||
* <li>SERVICE_CODE(String) - 服务码 </li> | |||
* <li>EXPIRED_DATE(String) - 卡片有效期 </li> | |||
* </ul> | |||
*/ | |||
void onSuccess(in Bundle track); | |||
/** | |||
* 刷卡失败回调 | |||
* @param error - 错误码 | |||
* <ul> | |||
* <li>SERVICE_CRASH(99) - 服务崩溃 </li> | |||
* <li>REQUEST_EXCEPTION(100) - 请求异常</li> | |||
* <li>MAG_SWIPE_ERROR(1) - 刷卡失败</li> | |||
* </ul> | |||
* @param message - 错误描述 | |||
*/ | |||
void onError(int error,String message ); | |||
/** | |||
* 刷卡超时回调 | |||
*/ | |||
void onTimeout(); | |||
} |
@@ -0,0 +1,30 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* PBOC流程联机应答数据处理 | |||
* @author: baoxl | |||
*/ | |||
interface OnlineResultHandler { | |||
/** | |||
* 联机应答数据处理回调<br/> | |||
* @param result - 处理结果 | |||
* <ul> | |||
* <li>ONLINE_RESULT_TC(0) - 联机成功 </li> | |||
* <li>ONLINE_RESULT_AAC(1) - 联机拒绝 </li> | |||
* <li>ONLINE_RESULT_OFFLINE_TC(101) - 联机失败,脱机成功 </li> | |||
* <li>ONLINE_RESULT_SCRIPT_NOT_EXECUTE(102) - 脚本未执行 </li> | |||
* <li>ONLINE_RESULT_SCRIPT_EXECUTE_FAIL(103) - 脚本执行失败 </li> | |||
* <li>ONLINE_RESULT_NO_SCRIPT(104) - 联机失败,未下送脚本 </li> | |||
* <li>ONLINE_RESULT_TOO_MANY_SCRIPT(105) - 联机失败,脚本超过1个 </li> | |||
* <li>ONLINE_RESULT_TERMINATE(106) - 联机失败,交易终止(GAC返回非9000,要提示交易终止,0x8F) </li> | |||
* <li>ONLINE_RESULT_ERROR(107) - 联机失败,EMV内核错误</li> | |||
* </ul> | |||
* @param data - 结果数据 | |||
* <ul> | |||
* <li>TC_DATA(String) - 交易结果TLV数据 </li> | |||
* <li>SCRIPT_DATA(String) - 脚本结果TLV数据 </li> | |||
* <li>REVERSAL_DATA(String) - 冲正TLV数据 </li> | |||
* </ul> | |||
*/ | |||
void onProccessResult(int result, in Bundle data); | |||
} |
@@ -0,0 +1,100 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* PBOC交易流程回调接口 | |||
* @author: baoxl | |||
*/ | |||
interface PBOCHandler { | |||
/** | |||
* 请求输入金额 | |||
*/ | |||
void onRequestAmount(); | |||
/** | |||
* 请求应用选择 | |||
* @param appList - 应用列表 | |||
*/ | |||
void onSelectApplication(in List<String> appList); | |||
/** | |||
* 要求确认卡信息 | |||
* @param info - 卡片信息 | |||
* <ul> | |||
* <li>PAN(String) - 主账号(卡号)</li> | |||
* <li>TRACK2(String) - 磁道2数据 </li> | |||
* <li>CARD_SN(String) - 卡片序列号 </li> | |||
* <li>SERVICE_CODE(String) - 服务码 </li> | |||
* <li>EXPIRED_DATE(String) - 卡片有效期 </li> | |||
* </ul> | |||
*/ | |||
void onConfirmCardInfo(in Bundle info); | |||
/** | |||
* 要求输入PIN | |||
* @param isOnlinePin 是否是联机pin | |||
* @param retryTimes 脱机pin的输入次数 | |||
*/ | |||
void onRequestInputPIN(boolean isOnlinePin, int retryTimes); | |||
/** | |||
* 要求确认持卡人证件 | |||
* @param certType - 认证类型 | |||
* @param certInfo - 认证信息 | |||
*/ | |||
void onConfirmCertInfo(String certType, String certInfo); | |||
/** | |||
* 联机处理请求 | |||
* @param aaResult 卡片分析结果,联机数据 | |||
* <ul> | |||
* <li>RESULT(int) - 结果类型:QPBOC_ARQC(201) - qPBOC联机请求; AARESULT_ARQC(2) - 行为分析结果ARQC</li> | |||
* <li>ARQC_DATA(String) - 联机请求卡片55域数据 </li> | |||
* <li>REVERSAL_DATA(String) - IC卡冲正TLV数据 </li> | |||
* </ul> | |||
*/ | |||
void onRequestOnlineProcess(in Bundle aaResult); | |||
/** | |||
* PBOC交易结果,见于简易流程,qPBOC,以及交易过程失败等 | |||
* @param result - 交易结果 | |||
* <ul> | |||
* <li>EMV_COMPLETE(9) - EMV简易流程结束 </li> | |||
* <li>EMV_ERROR(11) - EMV内核错误</li> | |||
* <li>EMV_FALLBACK(12) - FALLBACK </li> | |||
* <li>EMV_DATA_AUTH_FAIL(13) - 脱机数据认证失败 </li> | |||
* <li>EMV_APP_BLOCKED(14) - 应用被锁定 </li> | |||
* <li>EMV_NOT_ECCARD(15) - 非电子现金卡 </li> | |||
* <li>EMV_UNSUPPORT_ECCARD(16) - 该交易不支持电子现金卡 </li> | |||
* <li>EMV_AMOUNT_EXCEED_ON_PURELYEC(17) - 纯电子现金卡消费金额超限 </li> | |||
* <li>EMV_SET_PARAM_ERROR(18) - 参数设置错误(9F7A) </li> | |||
* <li>EMV_PAN_NOT_MATCH_TRACK2(19) - 主账号与二磁道不符 </li> | |||
* <li>EMV_CARD_HOLDER_VALIDATE_ERROR(20) - 持卡人认证失败 </li> | |||
* <li>EMV_PURELYEC_REJECT(21) - 纯电子现金卡被拒绝交易 </li> | |||
* <li>EMV_BALANCE_INSUFFICIENT(22) - 余额不足 </li> | |||
* <li>EMV_AMOUNT_EXCEED_ON_RFLIMIT_CHECK(23) - 交易金额超过非接限额检查 </li> | |||
* <li>EMV_CARD_BIN_CHECK_FAIL(24) - 卡BIN检查失败 </li> | |||
* <li>EMV_CARD_BLOCKED(25) - 卡被锁 </li> | |||
* <li>EMV_MULTI_CARD_ERROR(26) - 多卡冲突 </li> | |||
* <li>EMV_BALANCE_EXCEED(27) - 余额超出 </li> | |||
* <li>EMV_RFCARD_PASS_FAIL(60) - 挥卡失败 </li> | |||
* <li>EMV_IN_QPBOC_PROCESS(99) - QPBOC流程处理中 </li> | |||
* | |||
* <li>AARESULT_TC(0) - 行为分析结果,交易批准(脱机)</li> | |||
* <li>AARESULT_AAC(1) - 行为分析结果,交易拒绝 </li> | |||
* <li>QPBOC_AAC(202) - qPBOC交易结果,交易拒绝</li> | |||
* <li>QPBOC_ERROR(203) - qPBOC交易结果,交易失败 </li> | |||
* <li>QPBOC_TC(204) - qPBOC交易结果,交易批准 </li> | |||
* <li>QPBOC_CONT(205) - qPBOC结果,转接触式卡 </li> | |||
* <li>QPBOC_NO_APP(206) - qPBOC交易结果,无应用(可转UP Card)</li> | |||
* <li>QPBOC_NOT_CPU_CARD(207) - qPBOC交易结果,该卡非TYPE B/PRO卡</li> | |||
* </ul> | |||
* | |||
* @param data 交易结果数据 | |||
* <ul> | |||
* <li>TC_DATA(String) - IC卡交易批准卡片返回数据 </li> | |||
* <li>REVERSAL_DATA(String) - IC卡冲正数据 </li> | |||
* <li>ERROR(String) - 错误描述(PBOC流程错误返回) </li> | |||
* </ul> | |||
*/ | |||
void onTransactionResult(int result, in Bundle data); | |||
} |
@@ -0,0 +1,32 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* PIN输入过程监听器 | |||
* @author: baoxl | |||
*/ | |||
interface PinInputListener { | |||
/** | |||
* 按键按压事件 | |||
* @param len - 已输入密码长度 | |||
* @param key - 当前的Key值 | |||
*/ | |||
void onInput(int len, int key); | |||
/** | |||
* 用户确认PIN输入时调用 | |||
* @param data - pin码,输入为空时候,为null | |||
* @param isNonePin - 输入为空的时候为true | |||
*/ | |||
void onConfirm(in byte[] data, boolean isNonePin); | |||
/** | |||
* 取消PIN输入时调用 | |||
*/ | |||
void onCancel(); | |||
/** | |||
* 错误时回调 | |||
* @param errorCode - 错误码 | |||
*/ | |||
void onError(int errorCode); | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
public class PinpadKeyType{ | |||
// ABC中密钥类型0: MASTER KEY | |||
public static final int MASTERKEY = 0; | |||
//1:MAC key | |||
public static final int MACKEY = 1; | |||
// 2:PIN key | |||
public static final int PINKEY = 2; | |||
//3:TDK key | |||
public static final int TDKEY = 3; | |||
} |
@@ -0,0 +1,10 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
public class PrinterConfig { | |||
/**字体,默认小字体**/ | |||
public static final String BUNDLE_PRINT_FONT = "font"; | |||
/**对齐方式,默认左对齐**/ | |||
public static final String BUNDLE_PRINT_ALIGN = "align"; | |||
} |
@@ -0,0 +1,18 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 打印结果监听器 | |||
* @author: baoxl | |||
*/ | |||
interface PrinterListener { | |||
/** | |||
* 打印成功回调 | |||
*/ | |||
void onFinish(); | |||
/** | |||
* 打印失败回调 | |||
* @param error - 错误码 | |||
*/ | |||
void onError(int error); | |||
} |
@@ -0,0 +1,37 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 非接寻卡过程监听接口定义 | |||
* @author: baoxl | |||
*/ | |||
interface RFSearchListener { | |||
/** | |||
* 检测到磁条卡 | |||
* @param cardType - 卡类型 | |||
* <ul> | |||
* <li>S50_CARD(0x00) - S50卡</li> | |||
* <li>S70_CARD(0x01) - S70卡</li> | |||
* <li>PRO_CARD(0x02) - PRO卡</li> | |||
* <li>S50_PRO_CARD(0x03) - 支持S50驱动与PRO驱动的PRO卡</li> | |||
* <li>S70_PRO_CARD(0x04) - 支持S70驱动与PRO驱动的PRO卡 </li> | |||
* <li>CPU_CARD(0x05) - CPU卡</li> | |||
* </ul> | |||
*/ | |||
void onCardPass(int cardType); | |||
/** | |||
* 寻卡失败回调 | |||
* @param error - 错误码 | |||
* <ul> | |||
* <li>ERROR_TRANSERR(0xA2) - 通讯错误 </li> | |||
* <li>ERROR_PROTERR(0xA3) - 卡片返回数据不符合规范要求</li> | |||
* <li>ERROR_MULTIERR(0xA4) - 感应区内多卡存在</li> | |||
* <li>ERROR_CARDTIMEOUT(0xA7) - 超时无响应</li> | |||
* <li>ERROR_CARDNOACT(0xB3) - Pro卡或者TypeB卡未激活</li> | |||
* <li>ERROR_MCSERVICE_CRASH(0xff01) - 主控服务异常</li> | |||
* <li>ERROR_REQUEST_EXCEPTION(0xff02) - 请求异常</li> | |||
* </ul> | |||
* @param message - 错误描述 | |||
*/ | |||
void onFail(int error, String message); | |||
} |
@@ -0,0 +1,30 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 扫码器扫码结果监听器 | |||
* @author: baoxl | |||
*/ | |||
interface ScannerListener { | |||
/** | |||
* 扫码成功回调 | |||
* @param barcode - 条码 | |||
*/ | |||
void onSuccess(String barcode); | |||
/** | |||
* 扫码出错 | |||
* @param error - 错误码 | |||
* @param message - 错误描述 | |||
*/ | |||
void onError(int error, String message); | |||
/** | |||
* 扫码超时回调 | |||
*/ | |||
void onTimeout(); | |||
/** | |||
* 扫码取消回调 | |||
*/ | |||
void onCancel(); | |||
} |
@@ -0,0 +1,3 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
parcelable TusnData; |
@@ -0,0 +1,75 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
import android.os.Parcel; | |||
import android.os.Parcelable; | |||
/** | |||
* TUSN数据 | |||
* @author baoxl | |||
* | |||
*/ | |||
public class TusnData implements Parcelable { | |||
public static final Parcelable.Creator<TusnData> CREATOR = new Parcelable.Creator<TusnData>() { | |||
@Override | |||
public TusnData createFromParcel(Parcel source) { | |||
return new TusnData(source); | |||
} | |||
@Override | |||
public TusnData[] newArray(int size) { | |||
return new TusnData[size]; | |||
} | |||
}; | |||
/** 终端类型: 1为旧终端 2为新终端 */ | |||
private int mTerminalType; | |||
/** 使用终端TUSN_AUK对传入随机数与TUSN进行MAC运算的结果,对于旧终端,为8个空格 */ | |||
private String mMac; | |||
/** 唯一终端序列号 */ | |||
private String mTusn; | |||
protected TusnData(Parcel source) { | |||
mTerminalType = source.readInt(); | |||
mMac = source.readString(); | |||
mTusn = source.readString(); | |||
} | |||
public TusnData() {} | |||
public void setTerminalType(int type) { | |||
mTerminalType = type; | |||
} | |||
public void setMac(String mac) { | |||
mMac = mac; | |||
} | |||
public void setTusn(String tusn) { | |||
mTusn = tusn; | |||
} | |||
public String getMac() { | |||
return mMac; | |||
} | |||
public String getTusn() { | |||
return mTusn; | |||
} | |||
public int getTerminalType() { | |||
return mTerminalType; | |||
} | |||
@Override | |||
public int describeContents() { | |||
return 0; | |||
} | |||
@Override | |||
public void writeToParcel(Parcel dest, int flags) { | |||
dest.writeInt(mTerminalType); | |||
dest.writeString(mMac); | |||
dest.writeString(mTusn); | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
package com.icbc.smartpos.deviceservice.aidl; | |||
/** | |||
* 读手机芯片卡结果监听器 | |||
* @author: baoxl | |||
*/ | |||
interface UPCardListener { | |||
/** | |||
* 读卡成功回调 | |||
* @param data - 芯片卡卡数据 | |||
* <ul> | |||
* <li>PAN(String) - 主账号(卡号)</li> | |||
* <li>TRACK2(String) - 磁道2数据 </li> | |||
* <li>TRACK3(String) - 磁道3数据</li> | |||
* <li>CARD_SN(String) - 卡片序列号</li> | |||
* <li>EXPIRED_DATE(String) - 卡片有效期 </li> | |||
* <li>TLV_DATA(String) - 带标签(DF32,DF33,DF34)TLV数据</li> | |||
* </ul> | |||
*/ | |||
void onRead(out Bundle data); | |||
/** | |||
* 读卡错误回调 | |||
* @param error - 错误码 | |||
* <ul> | |||
* <li>ERROR_DETECT_CARD(1) - 识别卡失败</li> | |||
* <li>ERROR_READ_SN(2) - 读卡序列号失败</li> | |||
* <li>ERROR_READ_TRACK(3) - 读卡信息失败</li> | |||
* <li>ERROR_SERVICE_CRASH(4) - 设备服务异常</li> | |||
* <li>ERROR_NULL_DRIVER(5) - 非接驱动为null</li> | |||
* </ul> | |||
* @param message - 错误描述 | |||
*/ | |||
void onError(int error, String message); | |||
} |
@@ -0,0 +1,15 @@ | |||
package com.icbc.smartpos.transservice.aidl; | |||
import com.icbc.smartpos.transservice.aidl.TransHandler; | |||
/** | |||
* 交易调用接口 | |||
* @author: icbc | |||
*/ | |||
interface ITransService { | |||
long startTrans(String transType, | |||
in Bundle ctrlData, | |||
in Bundle transData, | |||
in TransHandler handler); | |||
} |
@@ -0,0 +1,10 @@ | |||
package com.icbc.smartpos.transservice.aidl; | |||
/** | |||
* 交易流程回调接口 | |||
* @author: icbc | |||
*/ | |||
interface TransHandler { | |||
void onFinish(in Bundle baseResult, in Bundle transResult, in Bundle extraInfo); | |||
} |
@@ -0,0 +1,115 @@ | |||
package com.unionpay.CloudPosPaymentClient.aidl; | |||
interface ICloudPay{ | |||
//-----------收单---------- | |||
//消费 | |||
String payCash(String jsonData); | |||
//交易查询 | |||
String getPayInfo(String jsonData); | |||
//获取终端信息 | |||
String getPOSInfo(String jsonData); | |||
//签到 | |||
String signIn(String jsonData); | |||
//批结算 | |||
String settlement(String jsonData); | |||
//余额查询 | |||
String balanceQuery(String jsonData); | |||
//消费撤销 | |||
String consumeCancel(String jsonData); | |||
//账户验证 | |||
String accountVerify(String jsonData); | |||
//交易统计 | |||
String dealStatics(String jsonData); | |||
//交易明细查询 | |||
String detailQuery(String jsonData); | |||
//退货 | |||
String returnGoods(String jsonData); | |||
//-----------多渠道---------- | |||
//建立委托 | |||
String createCommission(String jsonData); | |||
//撤销委托 | |||
String undoCommission(String jsonData); | |||
//单笔代收 | |||
String singleCollection(String jsonData); | |||
//实时代付 | |||
String payForAnother(String jsonData); | |||
//代付确认 | |||
String payConfirm(String jsonData); | |||
//单笔限额和单日限额查询 | |||
String limitQuery(); | |||
//单笔限额和单日限额设置 | |||
String limitSet(String jsonData); | |||
//剩余限额查询 | |||
String limitLeft(); | |||
//结算统计 | |||
String dealStatistics(String jsonData); | |||
// 信用卡还款 | |||
String creditCardPayment(String jsonData); | |||
// 转帐 | |||
String transfer(String jsonData); | |||
// 手机充值 | |||
String phoneRecharge(String jsonData); | |||
//单笔撤消 | |||
String cancelSingleCollection(String jsonData); | |||
//水电煤缴费 | |||
String billPayment(String jsonData); | |||
//账单查询 | |||
String billQuery(String jsonData); | |||
//终端激活 | |||
String getAppTerms(); | |||
//-----------UPOP---------- | |||
// 代收 | |||
String takeCollection(String data); | |||
// 代付 | |||
String takePayment(String data); | |||
// 交易撤销 | |||
String cancelTransaction(String data); | |||
// 交易查询 | |||
String queryTransaction(String data); | |||
// 文件传输类交易 | |||
String transactByFile(String data); | |||
// 代收控制交易 | |||
String cancelControl(String data); | |||
//--------贷款类--------- | |||
// 贷款查询 | |||
String loanQuery(String jsonData); | |||
// 贷款试算 | |||
String loanCal(String jsonData); | |||
// 贷款还款 | |||
String loanPayment(String jsonData); | |||
// 社保缴费 | |||
String tipsLevyAgent(String jsonData); | |||
//助农取款 | |||
String withDrawal(String jsonData); | |||
//--------贷款类二期-------- | |||
// 帐单费用查询 | |||
String billQueryServlet(String jsonData); | |||
// 缴费有密 | |||
String billPaymentServletWithPin(String jsonData); | |||
// 缴费无密 | |||
String billPaymentServletWithoutPin(String jsonData); | |||
//--------银联钱包2.0-------- | |||
// 增值服务消费(先查后消费) | |||
String walletSec(String jsonData); | |||
// 增值服务撤销 | |||
String walletSecRevoke(String jsonData); | |||
// 增值服务退货 | |||
String walletSecReturn(String jsonData); | |||
//单笔限额和单日限额查询,支持应用商户号 | |||
String limitQueryWithAppInfo(String jsonData); | |||
//剩余限额查询 | |||
String limitLeftWithAppInfo(String jsonData); | |||
//扫码付接口 | |||
String scanPay(String jsonData); | |||
} |
@@ -0,0 +1,7 @@ | |||
package com.unionpay.facepay; | |||
import com.unionpay.facepay.IFacePayListener; | |||
interface IFacePayInterface { | |||
boolean facePay(String payInfo, IFacePayListener listener); | |||
} |