啟動流程 & 程式碼
1. 名詞定義
| 縮寫 | 描述 |
|---|---|
| GM App | Gateway Management app |
| WKN | Well-Know Name,有別於About的建立session方式 |
| ACL | Access Control List |
| Config file | 描述Alljoyn router的Policy(Allow or Deny) |
| SLS | Sessionless Signal |
- Initialize Bus At tachment:
Start&Connect
BusAttachment* bus = new BusAttachment("ConnectorApp", true);
bus->Start();
bus->Connect();
- Initialize authentication:
EnablePeerSecurity這個函數是用來啟動身分驗證與加密機制,使用前需要先Start。
keyListener.setPassCode("000000");
String keystore = "/opt/alljoyn/apps/" + wellknownName + "/store/.alljoyn_keystore.ks";
bus.EnablePeerSecurity("ALLJOYN_PIN_KEYX ALLJOYN_SRP_KEYX ALLJOYN_ECDHE_PSK", &keyListener, keystore.c_str(), false);
這裡所出現的keyListener是一種自定類別,繼承自
AuthListener。class SrpKeyXListener : public ajn::AuthListenerSrpKeyXListener keyListener;而
AuthListener為啟動驗證機制時,負責傳達Password或其他與驗證機制相關的Class。繼承
AuthListener時,有幾種virtual method可以實作,其中比較常用的有RequestCredentials/AuthenticationCompletevirtual bool RequestCredentials (const char *authMechanism, const char *peerName, uint16_t authCount, const char *userName, uint16_t credMask, >>Credentials &credentials)virtual void AuthenticationComplete (const char *authMechanism, const >>char *peerName, bool success)=0以下用
SrpKeyXListener改寫的內容做為範例: 首先是RequestCredentialsbool SrpKeyXListener::RequestCredentials(const char* authMechanism, const char* authPeer,uint16_t authCount, const char* userId, uint16_t credMask, Credentials& creds) { std::cout << "RequestCredentials for authenticating " << authPeer << " using mechanism " << authMechanism << std::endl; if (strcmp(authMechanism, "ALLJOYN_SRP_KEYX") == 0 || strcmp(authMechanism, "ALLJOYN_PIN_KEYX") == 0 || strcmp(authMechanism, "ALLJOYN_ECDHE_PSK") == 0) { if (credMask & AuthListener::CRED_PASSWORD) { if (authCount <= 3) { qcc::String passCodeFromGet; if (m_GetPassCode) { m_GetPassCode(passCodeFromGet); } std::cout << "RequestCredentials setPasscode to " << (m_GetPassCode ? passCodeFromGet.c_str() : m_PassCode.c_str()) << std::endl; creds.SetPassword(m_GetPassCode ? passCodeFromGet.c_str() : m_PassCode.c_str()); return true; } else { return false; } } } return false; }其中
Credentials為AuthListener的Inner class,作為傳遞認證資訊的關鍵角色。再來是
AuthenticationCompletevoid SrpKeyXListener::AuthenticationComplete(const char* authMechanism, const char* authPeer, bool success) { std::cout << "Authentication with " << authMechanism << (success ? " was successful" : " failed") << std::endl; }此處只有一行
cout,表明認證結果。當Interface的secure設為True,第一次調用此Interface下的任何member,都會產生認證要求。
以下是上述Code的認證執行輸出:
RequestCredentials for authenticating org.alljoyn.GWAgent.GMApp using mechanism ALLJOYN_ECDHE_PSK RequestCredentials setPasscode to 000000 Authentication with ALLJOYN_ECDHE_PSK was successful
- Initialize Connector
class MyApp : public GatewayConnector
MyApp myApp(&bus, wellknownName.c_str());
myApp.init();
繼承GatewayConnector時,有幾項Method需要實作。
virtual void shutdown() {…} virtual void mergedAclUpdated() {…} void receiveGetMergedAclAsync(QStatus unmarshalStatus,GatewayMergedAcl* response) {…}
基本上到目前為止,就已經完成一個Connector了。接著只需要跟Cloud互動,並在互動過程中適時的呼叫myApp.updateConnectionStatus(...)回報給GM App目前與雲端的連接狀態。GM App收到後,會發Signal給Controler,Controler收到後會呼叫GM AppInterface的Method取得狀態。
GatewayConnector這個Class透過WKN取得GM App的ProxyBusObject,其內部有2個Method與3個Signal,需要RegisterSignalHandler。ifc->AddMethod("GetMergedAcl", NULL, "a(obas)a(saya(obas))", "exposedServices,remotedApps"); ifc->AddMethod("UpdateConnectionStatus", "q", NULL, "connectionStatus", MEMBER_ANNOTATE_NO_REPLY); ifc->AddSignal("MergedAclUpdated", NULL, NULL); ifc->AddSignal("ShutdownApp", NULL, NULL);
Build好Connector後的下一個工作,就是建置Manifest.xml了。Manifest.xml描述Connector的Service、感興趣的Interface、Connector執行時的參數傳入。
是之後Control App用以建立ACL的依據。
以下為Manifest.xml範例結構。
<manifest xmlns="http://www.alljoyn.org/gateway/manifest">
<connectorId>dummyapp1</connectorId>
<friendlyName>dummyAppOne</friendlyName>
<packageName>dummyAppOne_0.0.1-1_ar71xx.ipk</packageName>
<version>0.0.1</version>
<minAjSdkVersion>3.4.0</minAjSdkVersion>
<exposedServices>
<object name="EmergencyNotifications">
<path>/emergency</path>
<isPrefix>false</isPrefix>
<interfaces>
<interface name="NotificationInterface">org.alljoyn.Notification</interface>
</interfaces>
</object>
<object name="WarningNotifications">
<path>/warning</path>
<isPrefix>false</isPrefix>
<interfaces>
<interface name="NotificationInterface">org.alljoyn.Notification</interface>
</interfaces>
</object>
</exposedServices>
<remotedServices>
<object name="AllObjectPaths">
<path>/</path>
<isPrefix>true</isPrefix>
<interfaces>
<interface name="NotificationInterface">org.alljoyn.Notification</interface>
<interface name="AboutInterface">org.alljoyn.About</interface>
<interface name="AboutIcon">org.alljoyn.Icon</interface>
<interface name="NotificationSuperInterface">org.alljoyn.NotificationSuper</interface>
<interface name="ConfigInterface">org.alljoyn.Config</interface>
</interfaces>
</object>
</remotedServices>
<executionInfo>
<executable>alljoyn-gwconnectorsample</executable>
<env_variables>
<variable name="LD_LIBRARY_PATH">/opt/alljoyn/apps/dummyapp1/lib</variable>
<variable name="ER_DEBUG">7</variable>
<variable name="INTERACTIVE_OFF">1</variable>
<variable name="TWITTER_SCRIPT">postTweet.sh</variable>
</env_variables>
<arguments>
</arguments>
</executionInfo>
</manifest>
有了Build好的Connector與Manifest.xml以後,接著就是安裝/移除Connector了。
安裝的第一步就是將lib、bin、Manifest.xml依照下列格式擺放。

之後將整包壓縮成一個tar檔案。
tar czf dummyApp1.tar.gz -C gatewayConnector/tar

接著透過官方提供的installPackage.sh/removePackage.sh進行安裝/移除
./installPackage.sh dummyApp1.tar.gz
installPackage.sh所做的事情很單純, 只是將tar檔裡的東西轉移到/opt/alljoyn/apps/$connectorId之中,然後創造一個Linux User,並將store這個Folder的擁有者,轉移到新創的Linux User下baseDir=/opt/alljoyn manifestFile=$tmpDir/Manifest.xml appBinDir=$tmpDir/bin connectorId=$(grep "<connectorId>" $manifestFile | sed -e "s/ *<connectorId>//" | sed -e "s/<\/connectorId *>//") connectorAppDir=$baseDir/apps/$connectorId if [ $? -ne 0 ]; then useradd $connectorId || exit 22 createdUser=1 fi chown -R "$connectorId" "$pkgInstallDir/store" || exit 23 chmod -R a+rx "$pkgInstallDir/bin" || exit 24 chmod -R a+rx "$pkgInstallDir/lib" || exit 25
經過上面安裝後,就會看到/opt/alljoyn/apps/$connectorId目錄,結構如下。
此時acls Folder內無東西。可以透過shell自行創造,或是啟動
GM App與Controller App後,透過Contorller App建立。
5.1. 啟動Router
在啟動GM App與Connectors之前,先確定alljoyn-daemon是否已經安裝並啟動。
先確認是否Build好的alljoyn-daemon放在/usr/bin/中,並寫好alljoyn.init放在/etc/init.d/alljoyn之中。之後執行service alljoyn start
以下為
alljoyn.init的start scriptstart() { if [ -f $PIDFILE ] && kill -0 $(cat $PIDFILE); then echo 'Service already running' >&2 return 1 fi echo 'Starting service…' >&2 local CMD="$SCRIPT --config-file=/opt/alljoyn/alljoyn-daemon.d/config.xml &> \"$LOGFILE\" & echo \$!" su -c "$CMD" $RUNAS > "$PIDFILE" echo 'Service started' >&2 }執行時所帶的參數
--config-file指出Router Config Policy的位置,此例為/opt/alljoyn/alljoyn-daemon.d/config.xml
注意Config File內,要include GM App執行時動態改變的Policy File,此例為gwagent-config.xml
<busconfig>
<type>alljoyn</type>
<property name="router_advertisement_prefix">org.alljoyn.BusNode</property>
<listen>unix:abstract=alljoyn</listen>
<listen>tcp:r4addr=0.0.0.0,r4port=0</listen>
<limit name="auth_timeout">5000</limit>
<limit name="max_incomplete_connections">16</limit>
<limit name="max_completed_connections">100</limit>
<limit name="max_untrusted_clients">100</limit>
<flag name="restrict_untrusted_clients">false</flag>
<ip_name_service>
<property interfaces="*"/>
<property disable_directed_broadcast="false"/>
<property enable_ipv4="true"/>
<property enable_ipv6="true"/>
</ip_name_service>
<include>/opt/alljoyn/alljoyn-daemon.d/gwagent-config.xml</include>
</busconfig>
Config File內所有可用的的Tag,與每個Tag的說明,請參閱以下網址。 XML Schema Routing Node Configuration File
確認完成後,執行Router:service alljoyn start
5.2. 啟動GM App
確認Config File 與start alljoyn-daemon以後,對於GM App我們也依法照做一次。
將Build好的alljoyn-gwagent放在/usr/bin/中,並寫好alljoyn-gwagent.init放在/etc/init.d/alljoyn-gwagent之中。之後執行service alljoyn-gwagent start
以下為
alljoyn-gwagent.init的start scriptstart() { if [ -f $PIDFILE ] && kill -0 $(cat $PIDFILE); then echo 'Service already running' >&2 return 1 fi echo 'Starting service…' >&2 local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!" su -c "$CMD" $RUNAS > "$PIDFILE" echo 'Service started' >&2 }注意:雖然此例沒有使用,但事實上可以帶入參數
--gwagent-policy-file指出GM App執行時動態產生的Config File,以及--apps-policy-dir指出根據ACL產生的Config File Folder以下為
GM App程式碼片段String policyFileOption = "--gwagent-policy-file="; String appsPolicyDirOption = "--apps-policy-dir="; for (int i = 1; i < argc; i++) { String arg(argv[i]); if (arg.compare(0, policyFileOption.size(), policyFileOption) == 0) { String policyFile = arg.substr(policyFileOption.size()); QCC_DbgPrintf(("Setting gatewayPolicyFile to: %s", policyFile.c_str())); gatewayMgmt->setGatewayPolicyFile(policyFile.c_str()); } if (arg.compare(0, appsPolicyDirOption.size(), appsPolicyDirOption) == 0) { String policyDir = arg.substr(appsPolicyDirOption.size()); QCC_DbgPrintf(("Setting appsPolicyDir to: %s", policyDir.c_str())); gatewayMgmt->setAppPolicyDir(policyDir.c_str()); } }例如:
start() { service_start /usr/bin/alljoyn-gwagent `--gwagent-policy-file`=/etc/alljoyn/gwagent/gwagent.conf `--apps-policy-dir`=/etc/alljoyn/gwagent-apps }如果沒有特別指明,則Default gwagent-policy-file:
/opt/alljoyn/alljoyn-daemon.d/gwagent-config.xmlapps-policy-dir:/opt/alljoyn/alljoyn-daemon.d/appsstatic const qcc::String GATEWAY_POLICIES_DIRECTORY = "/opt/alljoyn/alljoyn-daemon.d"; GatewayRouterPolicyManager::GatewayRouterPolicyManager() : m_AboutListenerRegistered(false), m_AutoCommit(false), m_gatewayPolicyFile(GATEWAY_POLICIES_DIRECTORY + "/gwagent-config.xml"), m_appPolicyDirectory(GATEWAY_POLICIES_DIRECTORY + "/apps") { }下圖為此範例的目錄結構:
config.xml:
gwagent-config.xml:
apps-policy-dir內的dummyapp1.xml:
確認完成後,執行GM App:service alljoyn-gwagent start
5.3. 驗證
透過ps -ef | grep alljoyn指令驗證

5.4. 目錄結構
/opt/alljoyn下的樹狀結構:

5.5. Controller App畫面
進入搜尋到的GM App:

進入Connector's acl列表

允許Connector的服務:

含有此Connector感興趣的Interface的Device:

允許Connector存取此Device:

ACL產生:

同時產生Config Policy:

config.xml:
gwagent-config.xml:
apps-policy-dir內的dummyapp1.xml:
