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