{-# LANGUAGE ScopedTypeVariables, FlexibleContexts, OverloadedStrings,
ForeignFunctionInterface, EmptyDataDecls #-}
module Network.ADB.Client (
Session,
withClientSession,
withConnect,
module Network.ADB.Transport
) where
import Network.ADB.Common
import Network.ADB.Transport
import Control.Applicative
import Control.Concurrent
import Control.Concurrent.MVar
import Control.Exception
import Control.Monad.Error
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Internal as BI
import Data.Map (Map)
import qualified Data.Map as M
import Data.Monoid
import Foreign
import Foreign.C
import Prelude hiding (read, catch)
withClientSession :: Transport (ErrorT TransportError IO)
-> (Session (ErrorT TransportError IO) -> ErrorT TransportError IO a)
-> ErrorT TransportError IO a
withClientSession tra code = do
negotiate tra
withSession tra code
negotiate :: (MonadError TransportError m, Applicative m, MonadIO m) =>
Transport m -> m ()
negotiate tra = do
write tra $ formatPacket $ Packet SYNC 1 0 B.empty
write tra $ formatPacket $ Packet CNXN a_VERSION mAX_PAYLOAD "host::\0"
auth <- liftIO adbAuthNew
awaitCNXN auth
where
awaitCNXN auth = do
pkt <- readPacket tra
case pktCommand pkt of
CNXN -> return ()
AUTH -> do
mReply <- liftIO $ adbAuth auth pkt
case mReply of
Just reply -> do
write tra $ formatPacket reply
Nothing -> return ()
awaitCNXN auth
_ -> awaitCNXN auth
data ADBAuth_Struct
type ADBAuth = Ptr ADBAuth_Struct
foreign import ccall unsafe "adb_auth_new" adbAuthNew
:: IO ADBAuth
foreign import ccall unsafe "adb_auth" _adb_auth
:: ADBAuth -> Ptr Word8 -> IO CInt
adbAuth :: ADBAuth -> Packet -> IO (Maybe Packet)
adbAuth auth pkt = do
pkt_c <- B.unsafeUseAsCStringLen (formatPacket pkt) $ \(pkt_c, len) -> do
p <- mallocBytes (4096 + 24)
BI.memcpy p (castPtr pkt_c) len
return p
ret <-_adb_auth auth pkt_c
if ret /= 0
then do
outLen <- peekElemOff (castPtr pkt_c :: Ptr Word32) 3
let olen = fromIntegral outLen + 24
bs' <- BI.create olen $ \p -> BI.memcpy p pkt_c olen
free pkt_c
case parsePacket bs' of
Left err -> fail $ "bad packet from adbAuth: "++err
Right opkt -> return $ Just opkt
else do
free pkt_c
return Nothing
withConnect :: Session (ErrorT TransportError IO)
-> C.ByteString
-> (Transport (ErrorT TransportError IO) -> ErrorT TransportError IO a)
-> ErrorT TransportError IO a
withConnect session@(Session allocLocalID writePkt readPkt) uri code = do
localID <- liftIO allocLocalID
writePkt $ Packet OPEN localID 0 (uri <> B.singleton 0)
let awaitResult = do
pkt <- readPkt
if pktCommand pkt == OKAY && pktArg1 pkt == localID
then return $ Just $ pktArg0 pkt
else if pktCommand pkt == CLSE && pktArg1 pkt == localID
then return $ Nothing
else awaitResult
mTransportID <- awaitResult
case mTransportID of
Nothing -> do
throwError $ ConnectionFailure "no response"
Just remoteID -> do
withConversation session localID remoteID code