Android之WIFI-扫描服务WifiScanningService分析

简介

WifiScanningService用于自动扫描wlan

源码

7.1

服务端和客户端通信的案例

WifiScanner 与 WifiScanningService的通信--AsyncChannel

WifiScanner构造方法

    public WifiScanner(Context context, IWifiScanner service, Looper looper) {
        mContext = context;
        mService = service;

        Messenger messenger = null;
        try {
            messenger = mService.getMessenger();//获取了WifiScannerService的Handler
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        if (messenger == null) {
            throw new IllegalStateException("getMessenger() returned null!  This is invalid.");
        }

        mAsyncChannel = new AsyncChannel();

        mInternalHandler = new ServiceHandler(looper);
        mAsyncChannel.connectSync(mContext, mInternalHandler, messenger);
        // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
        // synchronously, which causes WifiScanningService to receive the wrong replyTo value.
        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);//建立起通信
    }

详细分析
1.WifiScanningService开机启动

com.android.server.SystemServer
    mSystemServiceManager.startService(
                            "com.android.server.wifi.scanner.WifiScanningService");

2.WifiScanningService。其实现类为WifiScanningServiceImpl

public class WifiScanningService extends SystemService {

    static final String TAG = "WifiScanningService";
    private final WifiScanningServiceImpl mImpl;
    private final HandlerThread mHandlerThread;

    public WifiScanningService(Context context) {
        super(context);
        Log.i(TAG, "Creating " + Context.WIFI_SCANNING_SERVICE);
        mHandlerThread = new HandlerThread("WifiScanningService");
        mHandlerThread.start();
        mImpl = new WifiScanningServiceImpl(getContext(), mHandlerThread.getLooper(),
                WifiScannerImpl.DEFAULT_FACTORY, BatteryStatsService.getService(),
                WifiInjector.getInstance());
    }

    @Override
    public void onStart() {
        Log.i(TAG, "Publishing " + Context.WIFI_SCANNING_SERVICE);
        publishBinderService(Context.WIFI_SCANNING_SERVICE, mImpl);
    }

    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            Log.i(TAG, "Starting " + Context.WIFI_SCANNING_SERVICE);
            mImpl.startService();
        }
    }
}

3.实现类分析:WifiScanningServiceImpl

1)adb查看:adb shell dumpsys wifiscanner
2)com.android.server.wifi.scanner.WifiScanningServiceImpl
    public void startService() {
        mClientHandler = new ClientHandler(mLooper);
        mBackgroundScanStateMachine = new WifiBackgroundScanStateMachine(mLooper);
        mWifiChangeStateMachine = new WifiChangeStateMachine(mLooper);
        mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper);
        mPnoScanStateMachine = new WifiPnoScanStateMachine(mLooper);

        mContext.registerReceiver(
                new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                        int state = intent.getIntExtra(
                                WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_DISABLED);
                        if (DBG) localLog("SCAN_AVAILABLE : " + state);
                        if (state == WifiManager.WIFI_STATE_ENABLED) {
                            mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
                            mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
                            mPnoScanStateMachine.sendMessage(CMD_DRIVER_LOADED);
                        } else if (state == WifiManager.WIFI_STATE_DISABLED) {
                            mBackgroundScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
                            mSingleScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
                            mPnoScanStateMachine.sendMessage(CMD_DRIVER_UNLOADED);
                        }
                    }
                }, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE));

        mBackgroundScanStateMachine.start();
        mWifiChangeStateMachine.start();
        mSingleScanStateMachine.start();
        mPnoScanStateMachine.start();
    }
3)状态的改变通过监听广播WifiManager.WIFI_SCAN_AVAILABLE(wifi_scan_available)
注:
广播的状态跟WifiStateMachine.DriverStartedState绑定在一起
final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);

4.具体分析广播触发导致的业务分发。以打开wifi为例

当接受到信息为WifiManager.WIFI_STATE_ENABLED时,处理如下:
单独从WifiSingleScanStateMachine状态机说起:

1)状态4:

DefaultState  --- DriverStartedState  --- IdleState
                                      --- ScanningState

2)当接收到广播(打开wifi发送的广播)所带值为WifiManager.WIFI_STATE_ENABLED

mSingleScanStateMachine.sendMessage(CMD_DRIVER_LOADED);

a.默认初始状态为DefaultState,处理消息CMD_DRIVER_LOADED

WifiScanningServiceImpl.SingleScanStateMachine.DefaultState
        class DefaultState extends State {
            ······
            @Override
            public boolean processMessage(Message msg) {
                switch (msg.what) {
                    case CMD_DRIVER_LOADED:
                        transitionTo(mIdleState);//处理消息时,转化到新的状态
                        return HANDLED;
                    ······
                }

            }
        }
        1)transitionTo(mIdleState)
        先退后进。
        退:默认DefaultState,无可退
        进:DriverStartedState.enter  --> IdleState.enter
        
        class IdleState extends State {
            @Override
            public void enter() {
                tryToStartNewScan();//执行扫描
            }
            ···
        }
        
        void tryToStartNewScan() {
            ······
            if (mScannerImpl.startSingleScan(settings, this)) {//调用mScannerImpl,执行扫描动作。mScannerImpl = mScannerImplFactory.create(mContext, mLooper, mClock);即SupplicantWifiScannerImpl
                ······
                transitionTo(mScanningState);//执行成功,则切换状态
            } else {
                ······
            }
        }
    此方法分两部分分析:
    11)第一部分:mScannerImpl.startSingleScan(settings, this)
    SupplicantWifiScannerImpl
    public boolean startSingleScan(WifiNative.ScanSettings settings,
            WifiNative.ScanEventHandler eventHandler) {
        if (eventHandler == null || settings == null) {
            Log.w(TAG, "Invalid arguments for startSingleScan: settings=" + settings
                    + ",eventHandler=" + eventHandler);
            return false;
        }
        if (mPendingSingleScanSettings != null
                || (mLastScanSettings != null && mLastScanSettings.singleScanActive)) {
            Log.w(TAG, "A single scan is already running");
            return false;
        }
        synchronized (mSettingsLock) {
            mPendingSingleScanSettings = settings;
            mPendingSingleScanEventHandler = eventHandler;
            processPendingScans();//真正处理的扫描业务
            return true;
        }
    }
    
    private void processPendingScans() {
        synchronized (mSettingsLock) {
            ······
            if ((newScanSettings.backgroundScanActive || newScanSettings.singleScanActive)
                    && !allFreqs.isEmpty()) {
                ······
                boolean success = mWifiNative.scan(freqs, hiddenNetworkIdSet);//WifiNative是跟wpa_supplicant的接口
                if (success) {
                    ······
                } else {
                    ······
                }
            } else if (isHwPnoScanRequired()) {
                ····
            }
        }
    }
    
    com.android.server.wifi.WifiNative
    scan --> scanWithParams --> doBooleanCommand --> doBooleanCommandNative
    private boolean scanWithParams(String freqList, String hiddenNetworkIdList) {
        StringBuilder scanCommand = new StringBuilder();
        scanCommand.append("SCAN TYPE=ONLY");//可关注此日志。TAG:WifiNative-wlan0, SCAN TYPE=
        if (freqList != null) {
            scanCommand.append(" freq=" + freqList);
        }
        if (hiddenNetworkIdList != null) {
            scanCommand.append(" scan_id=" + hiddenNetworkIdList);
        }
        return doBooleanCommand(scanCommand.toString());
    }
    
    com_android_server_wifi_WifiNative.cpp
    android_net_wifi_doBooleanCommand --> doBooleanCommand  --> doCommand
    
    wifi.c(hardware/libhardware_legacy/wifi/wifi.c)
    wifi_command --> wifi_send_command --> wpa_ctrl_request
    
    wpa_supplicant
    wpa_ctrl_request
    
    至此,wpa_supplicant扫描业务已经分析结束,问题:扫描所得的结果,怎么被扫描服务获取呢?
    我们知道WifiMonitor是用来监听wpa_supplicant数据改变,而其他对象需要被WifiMonitor通知,则要调用WifiMonitor.registerHandler。
    SupplicantWifiScannerImpl调用mWifiNative.scan,那是不是SupplicantWifiScannerImpl有WifiMonitor的registerHandler?
    com.android.server.wifi.scanner.SupplicantWifiScannerImpl
    public SupplicantWifiScannerImpl(Context context, WifiNative wifiNative,
            ChannelHelper channelHelper, Looper looper, Clock clock) {
        ······
        mEventHandler = new Handler(looper, this);
        ······
        WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(),
                WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
        WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(),
                WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
    }
    具体WifiMonitor怎么监听怎么分发机制,这里我们先不提,可关注:WifiService之WifiMonitor深入分析。
    
    WifiMonitor.SCAN_RESULTS_EVENT代表扫描所得的结果的通道
    public boolean handleMessage(Message msg) {
        switch(msg.what) {
            ······
            case WifiMonitor.SCAN_RESULTS_EVENT:
                mAlarmManager.cancel(mScanTimeoutListener);
                pollLatestScanData();//拉取最新的wifi列表数据
                processPendingScans();
                break;
            default:
                // ignore unknown event
        }
        return true;
    }
    
    private void pollLatestScanData() {
        synchronized (mSettingsLock) {
            if (mLastScanSettings == null) {
                 // got a scan before we started scanning or after scan was canceled
                return;
            }

            if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId);
            ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults();//从wpa_supplicant拉取数据
            ······

            if (mLastScanSettings.backgroundScanActive) {
                ·······
            }

            if (mLastScanSettings.singleScanActive
                    && mLastScanSettings.singleScanEventHandler != null) {
                ······
                mLastScanSettings.singleScanEventHandler
                        .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);//通知WifiScanningServiceImpl.WifiSingleScanStateMachine
            }

            if (mLastScanSettings.hwPnoScanActive
                    && mLastScanSettings.pnoScanEventHandler != null) {
                ······
            }

            mLastScanSettings = null;
        }
    }
    此方法分两部分分析:
    111)mWifiNative.getScanResults()
    com.android.server.wifi.WifiNative
    getScanResults --> getRawScanResults --> doStringCommandWithoutLogging --> doStringCommandNative
    private String getRawScanResults(String range) {
        return doStringCommandWithoutLogging("BSS RANGE=" + range + " MASK=0x29d87");////可关注此日志。TAG:WifiNative-wlan0, BSS RANGE=
    }
    
    
    com_android_server_wifi_WifiNative.cpp
    android_net_wifi_doStringCommand --> doStringCommand --> doCommand
    
    wifi.c(hardware/libhardware_legacy/wifi/wifi.c)
    wifi_command --> wifi_send_command --> wpa_ctrl_request
    
    wpa_supplicant
    wpa_ctrl_request
    
    112)mLastScanSettings.singleScanEventHandler
                        .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE)
    com.android.server.wifi.scanner.WifiScanningServiceImpl
        public void onScanStatus(int event) {
            if (DBG) localLog("onScanStatus event received, event=" + event);
            switch(event) {
                case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE:
                    sendMessage(CMD_SCAN_RESULTS_AVAILABLE);//状态机的业务分发
                    break;
                ······
            }
        }
        此时分析12)价值来了
12)第二部分:transitionTo(mScanningState)
    com.android.server.wifi.scanner.WifiScanningServiceImpl
    先退后进。
    退:无,因为ScanningState和IdleState共同一DriverStartedState
    进:ScanningState.enter
        class ScanningState extends State {
            private WorkSource mScanWorkSource;

            @Override
            public void enter() {
                ······
            }

            @Override
            public void exit() {
                ······
            }

            @Override
            public boolean processMessage(Message msg) {
                switch (msg.what) {
                    case CMD_SCAN_RESULTS_AVAILABLE:
                        mWifiMetrics.incrementScanReturnEntry(
                                WifiMetricsProto.WifiLog.SCAN_SUCCESS,
                                mActiveScans.size());
                        reportScanResults(mScannerImpl.getLatestSingleScanResults());//把结果通知出去
                        mActiveScans.clear();
                        transitionTo(mIdleState);
                        return HANDLED;
                    ······
                }
            }
        }
        结合112)可知,最后来到processMessage的CMD_SCAN_RESULTS_AVAILABLE
        void reportScanResults(ScanData results) {
            ······

            WifiScanner.ParcelableScanData parcelableAllResults =
                    new WifiScanner.ParcelableScanData(allResults);
            for (RequestInfo<Void> entry : mSingleScanListeners) {
                logCallback("singleScanResults",  entry.clientInfo, entry.handlerId,
                        describeForLog(allResults));
                entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults);
            }
        }
        
        WifiScanningServiceImpl.ExternalClientInfo.reportEvent
        public void reportEvent(int what, int arg1, int arg2, Object obj) {
            if (!mDisconnected) {
                mChannel.sendMessage(what, arg1, arg2, obj);//把数据发送给客户端
            }
        }
        这里说完了扫描的服务的处理业务。接下来的关键在于客户端的业务处理

5.客户端怎么监听服务端数据的变化。以自动连接wifi为例
1)mSingleScanListeners为客户端的监听者。它是怎么收集监听者呢?

com.android.server.wifi.scanner.WifiScanningServiceImpl
弄清楚回到客户端(WifiScanner)与服务端(WifiScanningServiceImpl)通信的具体方法AsyncChannel,就明白:
    private class ClientHandler extends Handler {

        ClientHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            ······
            switch (msg.what) {
                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {//客户端和服务端建立连接时,进行的初始化操作
                    ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
                    if (client != null) {
                        logw("duplicate client connection: " + msg.sendingUid + ", messenger="
                                + msg.replyTo);
                        client.mChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                                AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
                        return;
                    }

                    AsyncChannel ac = new AsyncChannel();
                    ac.connected(mContext, this, msg.replyTo);

                    client = new ExternalClientInfo(msg.sendingUid, msg.replyTo, ac);
                    client.register();

                    ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                            AsyncChannel.STATUS_SUCCESSFUL);

                    localLog("client connected: " + client);
                    return;
                }
                ······
                case WifiScanner.CMD_REGISTER_SCAN_LISTENER:
                    logScanRequest("registerScanListener", ci, msg.arg2, null, null, null);
                    mSingleScanListeners.addRequest(ci, msg.arg2, null, null);//客户端怎么处理呢?
                    replySucceeded(msg);
                    break;
                ······
            
        }
    }

2)客户端WifiScanner怎么处理?
a.客户端监听

android.net.wifi.WifiScanner
    private class ServiceHandler extends Handler {
        ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            ···
            Object listener = getListener(msg.arg2);//这里获取监听对象。这里跟一般的监听方法不一样
            ···
            switch (msg.what) {
                ···
                case CMD_SCAN_RESULT :
                    if (DBG) Log.d(TAG, "CMD_SCAN_RESULT ");
                    ((ScanListener) listener).onResults(
                            ((ParcelableScanData) msg.obj).getResults());
                    break;
            
        }
    }

监听对象的来源?
因WifiConnectivityManager包裹了WifiScanner,我们先看WifiConnectivityManager
com.android.server.wifi.WifiConnectivityManager
    public WifiConnectivityManager(Context context, WifiStateMachine stateMachine,
                WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo,
                WifiQualifiedNetworkSelector qualifiedNetworkSelector,
                WifiInjector wifiInjector, Looper looper, boolean enable) {
        mStateMachine = stateMachine;
        mScanner = scanner;
        ·······
        // Register for all single scan results
        mScanner.registerScanListener(mAllSingleScanListener);//查看mAllSingleScanListener。客户端进行数据监听
        ·······
    }

b.客户端对监听数据进行处理

    private class AllSingleScanListener implements WifiScanner.ScanListener {
        ······
        @Override
        public void onResults(WifiScanner.ScanData[] results) {//数据回调处
            ······
            boolean wasConnectAttempted = handleScanResults(mScanDetails, "AllSingleScanListener");//自动连接的具体方法
            ······
        }
        ·······
    }
    
    private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
        localLog(listenerName + " onResults: start QNS");
        WifiConfiguration candidate =
                mQualifiedNetworkSelector.selectQualifiedNetwork(false,
                mUntrustedConnectionAllowed, scanDetails,
                mStateMachine.isLinkDebouncing(), mStateMachine.isConnected(),
                mStateMachine.isDisconnected(),
                mStateMachine.isSupplicantTransientState());//获取是否有可连接的网络。WifiQualifiedNetworkSelector涉及wifi评分机制。有兴趣的可自行分析
        mWifiLastResortWatchdog.updateAvailableNetworks(
                mQualifiedNetworkSelector.getFilteredScanDetails());
        mWifiMetrics.countScanResults(scanDetails);
        if (candidate != null) {//如果网络存在,则连接
            localLog(listenerName + ": QNS candidate-" + candidate.SSID);
            connectToNetwork(candidate);
            return true;
        } else {
            return false;
        }
    }
    
    private void connectToNetwork(WifiConfiguration candidate) {
        ······
        if (currentConnectedNetwork != null
                && (currentConnectedNetwork.networkId == candidate.networkId
                || currentConnectedNetwork.isLinked(candidate))) {
            localLog("connectToNetwork: Roaming from " + currentAssociationId + " to "
                        + targetAssociationId);
            mStateMachine.autoRoamToNetwork(candidate.networkId, scanResultCandidate);
        } else {
            localLog("connectToNetwork: Reconnect from " + currentAssociationId + " to "
                        + targetAssociationId);
            mStateMachine.autoConnectToNetwork(candidate.networkId, scanResultCandidate.BSSID);//调用WifiStateMachine的自动连接
        }
    }
    
    com.android.server.wifi.WifiStateMachine
    public void autoConnectToNetwork(int networkId, String bssid) {
        Thread.dumpStack();
        synchronized (mWifiReqCountLock) {
            if (hasConnectionRequests()) {
                sendMessage(CMD_AUTO_CONNECT, networkId, 0, bssid);//状态机开始处理
            }
        }
    }
    此时,WifiStateMachine处于DisconnectedState状态。
    
    根据状态机处理业务逻辑,子状态无法处理,则父状态处理。
    ConnectModeState
    class ConnectModeState extends State {

        @Override
        public void enter() {
            ······
        }

        @Override
        public void exit() {
            ······
        }

        @Override
        public boolean processMessage(Message message) {
                case CMD_AUTO_CONNECT:
                    ······
                    /* Save the network config */
                    logd("CMD_AUTO_CONNECT will save config -> " + config.SSID
                            + " nid=" + Integer.toString(netId));
                    result = mWifiConfigManager.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);//保存网络状态
                    ······
                    if (mWifiConfigManager.selectNetwork(config, /* updatePriorities = */ false,
                            lastConnectUid) && mWifiNative.reconnect()) {//选择网络并且重新连接
                        ······
                    } else {
                        ······
                    }
                    break;  
        }
    }
    
    问题:reconnect之后,怎么知道连接成功了呢?
    关注WifiMonitor对wpa_supplicant的监听。结果为:WifiMonitor发送事件CTRL-EVENT-CONNECTED出来,转化为Message.what为NETWORK_CONNECTION_EVENT
    
    问题:DisconnectedState怎么最后转化成了ConnectedState?
    DisconnectedState先切换到状态mObtainingIpState,再从mObtainingIpState切换到ConnectedState
    
    补充流程:
    保存网络
    mWifiConfigManager.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
    com.android.server.wifi.WifiConfigManager
    saveNetwork --> saveConfig --> mWifiConfigStore.saveConfig()
    
    com.android.server.wifi.WifiConfigStore
    saveConfig --> mWifiNative.saveConfig()
    
    com.android.server.wifi.WifiNative
    saveConfig --> doBooleanCommand --> doBooleanCommandNative
    关注日志。TAG:WifiNative-wlan0 SAVE_CONFIG
    
    选择网络
    mWifiConfigManager.selectNetwork(config, /* updatePriorities = */ false,
                            lastConnectUid)
    com.android.server.wifi.WifiConfigManager
    selectNetwork --> selectNetworkWithoutBroadcast --> mWifiConfigStore.selectNetwork(
                mConfiguredNetworks.getForCurrentUser(netId),
                mConfiguredNetworks.valuesForCurrentUser())
                
    com.android.server.wifi.WifiConfigStore
    selectNetwork --> mWifiNative.selectNetwork(config.networkId)
    
    com.android.server.wifi.WifiNative
    selectNetwork --> doBooleanCommand --> doBooleanCommandNative
    关注日志。TAG:WifiNative-wlan0 SELECT_NETWORK
    
    重新连接
    com.android.server.wifi.WifiNative
    reconnect --> doBooleanCommand --> doBooleanCommandNative
    关注日志。TAG:WifiNative-wlan0 RECONNECT
    
    小结:
    a.自动连接最后仍会回到WifiStateMachine的操作
    b.WifiMonitor监听了wpa_supplicant的消息发送

总结

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

推荐阅读更多精彩内容