Installer启动分析

Installer启动分析

本篇继续解析pms前置installer服务的启动
系统启动之后init进程会解析各种.rc文件最后以service形式拉起zygote,zygote会fork各种系统服务,比如SystemServer,SystemServer会启动多种关键服务。
这里不再赘述init进程启动及zygote、SystemServer是如何启动

SystemServer是如何启动installer的

入口是在main函数
/frameworks/base/services/java/com/android/server/SystemServer.java

public final class SystemServer {
    ...
    public static void main(String[] args) {
        new SystemServer().run();
    }
    ...
    private void run() {
        ...
        Looper.prepareMainLooper();
        ...
        startBootstrapServices();
        startCoreServices();
        startOtherServices();
        ...
        Looper.loop();
        ...
    }

    private void startBootstrapServices() {
        ...
        Installer installer = mSystemServiceManager.startService(Installer.class);
        ...
    }
    ...

}

略去部分细节,run主要启动各种服务,开启主线程Looper开启消息分发
Installer服务是在startBootstrapServices中启动的

Installer启动

接下来看SystemServiceManager#startService实现
/frameworks/base/services/java/com/android/server/SystemServiceManager.java

public class SystemServiceManager {
    ...
    public SystemService startService(String className) {
        final Class<SystemService> serviceClass;
        try {
            serviceClass = (Class<SystemService>)Class.forName(className);
        } catch (ClassNotFoundException ex) {
            ...
        }
        return startService(serviceClass);
    }
    ...
    public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
            ...
            //如果不是SystemService子类说明要启动的不是个系统服务
            if (!SystemService.class.isAssignableFrom(serviceClass)) {
                ...
            }
            final T service;
            try {
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            } catch (InstantiationException ex) {
                ...
            } catch (IllegalAccessException ex) {
                ..
            } catch (NoSuchMethodException ex) {
                ...
            } catch (InvocationTargetException ex) {
                ...
            }

            startService(service);
            return service;
        } finally {
            ...
        }
    }
    ...
    public void startService(@NonNull final SystemService service) {
        // Register it.
        mServices.add(service);
        ...
        try {
            service.onStart();
        } catch (RuntimeException ex) {
            ...
        }
        ...
    }
    ...
}

  • 看到整个调用流程就是拿到Installer.class然后调用构造函数初始化一个Installer

  • 最后先cache要启动的Installer,然后调用service.onStart()去启动服务,先看Installer的实现
    /frameworks/base/services/java/com/android/server/pm/Installer.java

public class Installer extends SystemService {
    ...
    private final boolean mIsolated;
    ...
    public Installer(Context context) {
        this(context, false);
    }

    public Installer(Context context, boolean isolated) {
        super(context);
        mIsolated = isolated;
    }
    ...
    @Override
    public void onStart() {
        if (mIsolated) {
            ...
        } else {
            connect();
        }
    }

    private void connect() {
        //获取installd的proxy贼复杂
        IBinder binder = ServiceManager.getService("installd");
        if (binder != null) {
            try {
                //死亡通知重连
                binder.linkToDeath(new DeathRecipient() {
                    @Override
                    public void binderDied() {
                        ...
                        connect();
                    }
                }, 0);
            } catch (RemoteException e) {
                binder = null;
            }
        }

        if (binder != null) {
            mInstalld = IInstalld.Stub.asInterface(binder);
            try {
                invalidateMounts();
            } catch (InstallerException ignored) {
                ...
            }
        } else {
            ...
        }
    }
    ...
}

  • 这里三件件事,获取installd守护进程,设置binder的死亡通知,然后去调用invalidateMounts判断sdcard状态

  • 接下来分析调用流程,前方车速较快,乘客请做好

ServiceManager.getService(“installd”)

/frameworks/base/core/java/android/os/ServiceManager.java

public final class ServiceManager {
    ...
    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }
        // Find the service manager
        //BinderInternal.getContextObject() 拿到bpBinder(0)
        //之后对bpBinder(0)做一层封装返回BinderProxy
        sServiceManager = ServiceManagerNative
             .asInterface(Binder
             .allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }

    ...
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(rawGetService(name));
            }
        } catch (RemoteException e) {
            ...
        }
        return null;
    }
    ...

    private static IBinder rawGetService(String name) throws RemoteException {
        ...
        final IBinder binder = getIServiceManager().getService(name);
        ...
        return binder;
    }
}

  • 首次调用sCache中如果没有缓存的server就通过rawGetService()去查找对应的服务

getIServiceManager()

getIServiceManager()调用比较复杂,我们先来看BinderInternal.getContextObject()实现

BinderInternal.getContextObject()

/frameworks/base/core/java/com/android/internal/os/BinderInternal.java

public class BinderInternal {
    ...
    public static final native IBinder getContextObject();
    ...
}

这个是个native方法,对应jni实现在/frameworks/base/core/jni/android_util_Binder.cpp

...
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}
...
static const JNINativeMethod gBinderInternalMethods[] = {
     /* name, signature, funcPtr */
    { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
    ...
};
...

jni中getContextObject对应的函数是android_os_BinderInternal_getContextObject,其中主要通过ProcessState::getContextObject(NULL)拿到bpBinder(0)也就是service_manager的代理对象,然后经过一层包装,最后返还给java层

ProcessState::getContextObject

/frameworks/native/libs/binder/ProcessState.cpp

...
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    //这里传入的handle为0
    return getStrongProxyForHandle(0);
}
...
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    ...

    handle_entry* e = lookupHandleLocked(handle);

    if (e != nullptr) {
        ...
        IBinder* b = e->binder;
        if (b == nullptr || !e->refs->attemptIncWeak(this)) {
            ...
            b = BpBinder::create(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            ...
        }
    }

    return result;
}
...

  • 可以看到实际上拿到的就是个BpBinder(0),具体流程可以参考笔者Binder分析那篇,这里不再赘述
javaObjectForIBinder

分析javaObjectForIBinder()实现之前,我们先来看一些jni初始化相关的java层引用
/frameworks/base/core/jni/android_util_Binder.cpp

...
static struct binderproxy_offsets_t
{
    // Class state.
    jclass mClass;
    jmethodID mGetInstance;
    jmethodID mSendDeathNotice;

    // Object state.
    jfieldID mNativeData;  // Field holds native pointer to BinderProxyNativeData.
} gBinderProxyOffsets;
...
int register_android_os_Binder(JNIEnv* env)
{
    ....
    if (int_register_android_os_BinderProxy(env) < 0)
        return -1;
    ...
}
...
const char* const kBinderProxyPathName = "android/os/BinderProxy";

static int int_register_android_os_BinderProxy(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, "java/lang/Error");
    gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);

    clazz = FindClassOrDie(env, kBinderProxyPathName);
    //BinderProxy
    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    //BinderProxy#getInstance
    gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, 
        clazz, "getInstance", "(JJ)Landroid/os/BinderProxy;");
    ...
    //BinderProxy#mNativeData
    gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz,
         "mNativeData", "J");
    //Class
    clazz = FindClassOrDie(env, "java/lang/Class");
    //Class#getName
    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, 
        "getName", "()Ljava/lang/String;");

    return RegisterMethodsOrDie(
        env, kBinderProxyPathName,
        gBinderProxyMethods, NELEM(gBinderProxyMethods));
}
...

  • jni文件初始化时候会去解析拿到BinderProxy的class、getInstance、mNativeData等信息并存到gBinderProxyOffsets这个struct中
    接下来看javaObjectForIBinder()的实现
    /frameworks/base/core/jni/android_util_Binder.cpp
BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
    return (BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);
}
...

struct BinderProxyNativeData {

    // The native IBinder proxied by this BinderProxy.
    sp<IBinder> mObject;

    sp<DeathRecipientList> mOrgue;  // Death recipients for mObject.
};

...

jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    //一系列校验
    ...

    //封装BinderProxyNativeData
    BinderProxyNativeData* nativeData = new BinderProxyNativeData();
    nativeData->mOrgue = new DeathRecipientList;
    nativeData->mObject = val;

    //调用BinderProxy#getInstance(nativeData,val.get())
    jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
            gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
    if (env->ExceptionCheck()) {
        ...
        return NULL;
    }
    BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
    ...

    return object;
}

  • 这里val指向的是bpBinder(0),这里构造了BinderProxyNativeData结构体用来存bpBinder(0)与死亡通知队列DeathRecipientList,之后通过调用BinderProxy#getInstance()将nativeData的地址及bpBinder(0)地址传入,最后经过数据初始化然后返回一个BinderProxy对象
    /frameworks/base/core/java/android/os/BinderProxy.java
public final class BinderProxy implements IBinder {
    ...
    private static BinderProxy getInstance(long nativeData, long iBinder) {
        BinderProxy result;
        synchronized (sProxyMap) {
            try {
                result = sProxyMap.get(iBinder);
                if (result != null) {
                    return result;
                }
                result = new BinderProxy(nativeData);
            } catch (Throwable e) {
                ...
            }
            ...
            sProxyMap.set(iBinder, result);
        }
        return result;
    }
    ...
}

BinderProxy#getInstance实际上就是cache了bpBinder(0)与BinderProxy对象的map,方便后续多次使用

ServiceManagerNative#asInterface

回到getIServiceManager(),Binder.allowBlocking只是将BinderProxy的mWarnOnBlocking置位false

public static IBinder allowBlocking(IBinder binder) {
        try {
            if (binder instanceof BinderProxy) {
                ((BinderProxy) binder).mWarnOnBlocking = false;
            }else if(...){
                ...
            }
        } catch (RemoteException ignored) {
        }
        return binder;
    }

最终ServiceManagerNative#asInterface拿到返回一个ServiceManagerProxy并赋值给sServiceManager
/frameworks/base/core/java/android/os/ServiceManagerNative.java

public abstract class ServiceManagerNative extends Binder implements IServiceManager
{
    ...
    static public IServiceManager asInterface(IBinder obj)
    {
        if (obj == null) {
            return null;
        }
        IServiceManager in =
            (IServiceManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
        //obj就是持有BpBinder(0)的BinderProxy对象
        return new ServiceManagerProxy(obj);
    }
    ...
}

ServiceManagerProxy#getService

接下来我们看ServiceManagerProxy#getService
/frameworks/base/core/java/android/os/ServiceManagerNative.java

public abstract class ServiceManagerNative extends Binder implements IServiceManager
{
    ...
    class ServiceManagerProxy implements IServiceManager {
        public ServiceManagerProxy(IBinder remote) {
            mRemote = remote;
        }
        ...
        public IBinder getService(String name) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken(IServiceManager.descriptor);
            data.writeString(name);
            mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
            //service_manager返回的installd对象
            IBinder binder = reply.readStrongBinder();
            reply.recycle();
            data.recycle();
            return binder;
        }
        ...
    }
    ...
}

ServiceManagerProxy是一个内部类,mRemote就是前文中提到的持有BpBinder(0)的BinderProxy对象,最终是一个跨进程通信,这里name是installd

BinderProxy#transact

BinderProxy最终会调用到transactNative,直接来看jni
/frameworks/base/core/jni/android_util_Binder.cpp

...
static const JNINativeMethod gBinderProxyMethods[] = {
    ...
    {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact}
    ...
}

-对应的JNI函数为android_os_BinderProxy_transact
/frameworks/base/core/jni/android_util_Binder.cpp

...
static jboolean android_os_BinderProxy_transact(JNIEnv* env, 
    jobject obj, jint code, jobject dataObj, 
        jobject replyObj, jint flags)
{
    ...
    Parcel* data = parcelForJavaObject(env, dataObj);
    ...
    Parcel* reply = parcelForJavaObject(env, replyObj);
    ...
    //前面存到BinderProxyNativeData中拿到bpBinder
    IBinder* target = getBPNativeData(env, obj)->mObject.get();
    ...
    status_t err = target->transact(code, *data, reply, flags);
    ...
}
...

这里实际上就是拿到前面获取过的BpBinder(0),然后调用BpBinder(0)#transact()去发起binder查找通信,service服务的查找清参考老罗的Android的Binder系列
回到ServiceManagerProxy#getService,最终会从ipc通信reply中拿到一个IBinder对象

Parcel#readStrongBinder

/frameworks/base/core/java/android/os/Parcel.java

public final class Parcel {
    ... 
    public final IBinder readStrongBinder() {
        return nativeReadStrongBinder(mNativePtr);
    }
    ...
}

查看jni调用
/frameworks/base/core/jni/android_os_Parcel.cpp

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        //封装成BinderProxy返回给java层
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}
...
static const JNINativeMethod gParcelMethods[] = {
    ...
    {"nativeReadStrongBinder",    "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder}
    ...
}

android_os_Parcel_readStrongBinder主要是就是调用parcel->readStrongBinder拿到对应的BpBinder引用对象

Parcel::readStrongBinder

接下来我们来看是怎么解析到servce_manager返回的BpBinder代理对象的
/frameworks/native/libs/binder/Parcel.cpp

status_t Parcel::readStrongBinder(sp<IBinder>* val) const
{
    status_t status = readNullableStrongBinder(val);
    if (status == OK && !val->get()) {
        status = UNEXPECTED_NULL;
    }
    return status;
}

status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
{
    return unflatten_binder(ProcessState::self(), *this, val);
}
...
status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->hdr.type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(nullptr, *flat, in);
            case BINDER_TYPE_HANDLE:
                //前文分析过这个就是获取handle持有的BpBinder
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

调用链readStrongBinder->readNullableStrongBinder->unflatten_binder,我在Binder那篇中关于binder通信数据传递有一张图

image.png

同样适用于这里,通过service_manager查找服务返回的type是BINDER_TYPE_HANDLE,这里不解释为啥,自己去看/frameworks/native/cmds/servicemanager/service_manager.c中查找服务的实现
回到android_os_Parcel_readStrongBinder,接下来调用javaObjectForIBinder讲查到的installd的BpBinder封装成一个BinderProxy返回给java层,在这之后Java层就可以通过这个BinderProxy与installd通信

Installer#invalidateMounts

回到Installer#connect中,有了installd的代理Binder对象以后,先绑定死亡通知重连,之后讲installd的代理Binder对象再包一层,赋值给mInstalld最后调用invalidateMounts
/frameworks/base/services/core/java/android/server/pm/installer.java

public class Installer extends SystemService {
    ...
    public void invalidateMounts() throws InstallerException {
        ...
        try {
            mInstalld.invalidateMounts();
        } catch (Exception e) {
            throw InstallerException.from(e);
        }
    }
    ...
}

mInstalld怎么去做ipc调用的不再细述,上一篇installd进程启动分析中,installd启动时候注册的Service是InstalldNativeService

InstalldNativeService::invalidateMounts

InstalldNativeService定义在/frameworks/native/cmds/installd/InstalldNativeService.h中,不再贴了读者感兴趣可以看其定义,直接来看invalidateMounts实现
/frameworks/native/cmds/installd/InstalldNativeService.cpp

...
binder::Status InstalldNativeService::invalidateMounts() {
    ENFORCE_UID(AID_SYSTEM);
    std::lock_guard<std::recursive_mutex> lock(mMountsLock);
    //清理cache
    mStorageMounts.clear();

//BYPASS_QUOTA == 0
#if !BYPASS_QUOTA
    if (!InvalidateQuotaMounts()) {
        ...
    }
#endif
    //检查mounts授权
    std::ifstream in("/proc/mounts");
    if (!in.is_open()) {
        return error("Failed to read mounts");
    }

    std::string source;
    std::string target;
    std::string ignored;
    /**
     * tmpfs /dev tmpfs rw,seclabel,nosuid,relatime,size=1812472k,nr_inodes=453118,mode=755 0 0
    devpts /dev/pts devpts rw,seclabel,relatime,mode=600,ptmxmode=000 0 0

     */
    while (!in.eof()) {
        //获取source devpts
        std::getline(in, source, ' ');
        //获取target路径 /dev/pts
        std::getline(in, target, ' ');
        std::getline(in, ignored);

#if !BYPASS_SDCARDFS
        //SDCARD 检查
        if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
            ...
            mStorageMounts[source] = target;
        }
#endif
    }
    return ok();
}
...
bool InvalidateQuotaMounts() {
    std::lock_guard<std::recursive_mutex> lock(mMountsLock);

    mQuotaReverseMounts.clear();

    std::ifstream in("/proc/mounts");
    if (!in.is_open()) {
        return false;
    }

    std::string source;
    std::string target;
    std::string ignored;
    while (!in.eof()) {
        std::getline(in, source, ' ');
        std::getline(in, target, ' ');
        std::getline(in, ignored);

        if (source.compare(0, 11, "/dev/block/") == 0) {
            struct dqblk dq;
            //Get disk quota limits and current usage for user or group id
            if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0,
                    reinterpret_cast<char*>(&dq)) == 0) {
                ...
                mQuotaReverseMounts[target] = source;
            }
        }
    }
    return true;
}
...

  • /proc/monuts文件长这样
image.png

解析过程中,例如第一行中
source = proc
target = /proc
ignored = proc rw,relatime,gid=3009,hidepid=2 0 0

  • 首先调用InvalidateQuotaMounts去检查用户磁盘配额quotactl并存到mQuotaReverseMounts中

  • 然后去sdcard是否已经mount授权,并存到mStorageMounts中

总结

至此,整个installer就启动完成,总结下来,installer回去查installd守护进程并将获取到的Binder代理对象cache到mInstalld中便于后续交互通信,启动过程中如果binder不小心跪了,会有重连策略,初始化最终的目的是解析用户磁盘授权+sdcard的授权

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345