前言

前阵子复习 AIDL 相关的知识,知道 AIDL 不支持 short 类型,但是我却发现同样使用 AIDL 的 Messenger 却可以。

这是怎么回事,本文将探索这其中原因。

short 的支持

我们知道 AIDL 是不支持 short 类型的,平常我们也不会去使用 short ,如果要用也可以用 int 来代替。

虽然 AIDL 不支持,但是在使用 Messenger 的时候,怀着好奇心在 Messenger 中的 data 里面放进去一个 short 类型数据,发现服务端可以收到,并正确打印。代码如下:

客户端

class MainActivity : AppCompatActivity() {
        private val TAG = MainActivity::class.simpleName
        var mMessenger:Messenger? = null

        val conn = object : ServiceConnection {
            override fun onServiceDisconnected(name: ComponentName?) {
                Log.d(TAG,"onServiceDisconnected")
            }

            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                mMessenger = Messenger(service)
                sendMsgToServer()
            }
        }

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            bindService(Intent(this, MessengerService::class.java), conn, Context.BIND_AUTO_CREATE)

        }

        private fun sendMsgToServer() {
            val msg = Message.obtain(null, 1)
            msg.data = Bundle().also {
                it.putShort("shortTest", 2)
            }
            mMessenger?.send(msg)
        }
}

服务端

class MessengerService : Service() {
    val mMessenger = Messenger(object : Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            val data = msg.data
            println(data.getShort("shortTest"))
        }
    })

    override fun onBind(intent: Intent?): IBinder? {
        return mMessenger.binder
    }
}

这就奇怪了,为什么同样使用 AIDL , Messenger 就可以使用 short ,反而 AIDL 不行?

这里我们发现 short 是放在 Bundle 中的, Bundle 实现了 Parcelable 。 而在 .aidl 中是直接写在文件中,基于这个差异于是猜想在 .aidl 文件中不能使用 short 类型,而自己写一个类,里面放 short 是可以的。

于是我写了如下代码,来验证一下

import android.os.Parcel;
import android.os.Parcelable;

class ShortMsg implements Parcelable {
    public short shortType;

    ShortMsg() {
        shortType = 2;
    }

    protected ShortMsg(Parcel in) {
        shortType = (short) in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt((int) shortType);
    }

    public static final Creator<ShortMsg> CREATOR = new Creator<ShortMsg>() {
        @Override
        public ShortMsg createFromParcel(Parcel in) {
            return new ShortMsg(in);
        }

        @Override
        public ShortMsg[] newArray(int size) {
            return new ShortMsg[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }
}

对应的 AIDL 文件

package com.dev.bins.aidl;

parcelable ShortMsg;

写一个方法来传递数据

package com.dev.bins.aidl;
import com.dev.bins.aidl.ShortMsg;

interface IShortInterface {
    void send(in ShortMsg msg);
}

对应的 Service

class ShortService : Service() {
    val binder = object: IShortInterface.Stub() {
        override fun send(msg: ShortMsg?) {
            println("short: ${msg?.shortType}")
        }
    }

    override fun onBind(intent: Intent?): IBinder? {
        return binder
    }
}

在 Activity 中发送 ShortMsg

class MainActivity : AppCompatActivity() {

    companion object {
        val TAG = MainActivity.javaClass.simpleName
    }

    var server : IShortInterface? = null

    val conn = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {
            Log.d(TAG,"onServiceDisconnected")
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d(TAG, "connect")
            server = IShortInterface.Stub.asInterface(service)
            val msg = ShortMsg()
            server?.send(msg)
        }

    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val intent = Intent(this, ShortService::class.java)
        bindService(intent, conn, Context.BIND_AUTO_CREATE)
    }
}

见证奇迹的时候,控制台打印了 short 的数据。

2021-05-28 23:39:42.845 16923-16936/com.dev.bins.aidl I/System.out: short: 2

到了这里就可以肯定了, short 不能在 .aidl 文件中使用,但是可以通过自定义类型来传递 short 类型的数据。

探秘 AIDL 不支持 short 的原因

如果我们在 aidl 文件中使用 short 会报如下错误

Failed to resolve 'short'

我们知道在编译的时候会把 .aidl 文件转成 .java 文件,转换使用的是 sdk/build-tools/30.0.1/aidl 工具来生成的,所以不能使用的原因在 aidl 这个工具里。 aidl 的源码在 /system/tools/aidl 中,我这里以 Android10 源码为例。

由于源码太多,我们从错误入手,使用 rg 搜索 Failed to resolve 幸运的发现只有一处,文件在 /system/tools/aidl/aidl_language.cpp 如下所示

bool Parser::Resolve() {
    bool success = true;
    for (AidlTypeSpecifier* typespec : unresolved_typespecs_) {
        if (!typespec->Resolve(typenames_)) {
            AIDL_ERROR(typespec) << "Failed to resolve '" << typespec->GetUnresolvedName() << "'";
            success = false;
            // don't stop to show more errors if any
        }
    }
    return success;
}

bool AidlTypeSpecifier::Resolve(android::aidl::AidlTypenames& typenames) {
  assert(!IsResolved());
  pair<string, bool> result = typenames.ResolveTypename(unresolved_name_);
  if (result.second) {
    fully_qualified_name_ = result.first;
  }
  return result.second;
}

从上面的代码可以看出来,问题出现在 typespec->Resolve 中,接着发现在 Resolve 中调用了 AidlTypenames.ResolveTypename ,对应文件在 /system/tools/aidl/aidl_typenames.cpp 中

// The built-in AIDL types..
// 没有发现 short 的身影
static const set<string> kBuiltinTypes = {
    "void", "boolean", "byte",           "char",         "int",
    "long", "float",   "double",         "String",       "List",
    "Map",  "IBinder", "FileDescriptor", "CharSequence", "ParcelFileDescriptor"};

static const map<string, string> kJavaLikeTypeToAidlType = {
    {"java.util.List", "List"},
    {"java.util.Map", "Map"},
    {"android.os.ParcelFileDescriptor", "ParcelFileDescriptor"},
};

bool AidlTypenames::IsBuiltinTypename(const string& type_name) {
    return kBuiltinTypes.find(type_name) != kBuiltinTypes.end() ||
        kJavaLikeTypeToAidlType.find(type_name) != kJavaLikeTypeToAidlType.end();
}

pair<string, bool> AidlTypenames::ResolveTypename(const string& type_name) const {
    if (IsBuiltinTypename(type_name)) {
        auto found = kJavaLikeTypeToAidlType.find(type_name);
        if (found != kJavaLikeTypeToAidlType.end()) {
            return make_pair(found->second, true);
        }
        return make_pair(type_name, true);
    }
    const AidlDefinedType* defined_type = TryGetDefinedType(type_name);
    if (defined_type != nullptr) {
        return make_pair(defined_type->GetCanonicalName(), true);
    } else {
        return make_pair(type_name, false);
    }
}

在 ResolveTypename 中,先调用了 IsBuiltinTypename 来判断是不是内置类型,而判断内置类型是通过查找 kBuiltinTypes 这个 Set 和 kJavaLikeTypeToAidlType 这个 Map 。显然这里就没有 short ,所以返回 false 。

接着调用 TryGetDefinedType ,这个函数是用于处理自定义类型的,显然也是没有 short 的,所以这里返回 nullptr ,最后整个函数返回 pair(type_name, false) ,所以最后会输出 Failed to resolve 'short'

分析到这里我们就知道了在把 .aidl 转换为 .java 的时候不支持 short ,所以在 .aidl 中不能使用的原因在于这里。

总结

今天我们发现同样基于 AIDL 的 Messenger 可以支持 short 类型,而 AIDL 却不可以。

接着我们从源码中找到了 AIDL 不支持的原因。