天天看点

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

a device running android os version 2.2 (froyo) or greater and running a chip based on the arm 5 (or greater) instruction set.

there are several samples provided as part of the alljoyn™ framework. the samples may be found in the alljoyn sdk in the java/samples folder.

start eclipse.

select file > import.

select general > existing

projects into workspace.

click next.

click browse near

the select root directory text box.

navigate to the samples folder and find the simple service sample (/java/samples/simple/service).

click ok.

click finish.

the simple service sample should now be imported into your workspace. the project may be giving you a few errors.

to remove the project errors:

set class compatibility to 5.0.

select the project in the package explorer window. press alt+enter to

bring up properties for the simple service.

select the java compiler option.

under the java compiler options, check the enable

project specific settings option.

set the compiler compliance level to 1.5.

rebuild the auto-generated gen folder.

click project > clean.

use the same process to import any sample of interest.

note: for an android project, some versions on eclipse require the need to right-click on the project, then selectandroid

tools > fix project properties. if you still encounter an error with the android project, right-click on the project, then select properties.

under this window, click on java build path in the left navigation, then select theorder

and export tab. ensure that the android private libraries contains a check, then clean

the project.

how to do this.)

go to your workspace and find the folder that contains the new android project.

the project folder should contain the following (it may contain more):

res folder

src folder

androidmanifest.xml file

create a new folder with the name libs.

copy the file /java/jar/alljoyn.jar to

the new libs folder.

in the libs folder, create another folder with the name armeabi.

copy the liballjoyn_java.so file from the alljoyn distribution to the armeabi folder.

the file is in the alljoyn distribution in the folder /java/libs/liballjoyn_java.so.

you should now have a project directory structure that is similar to this (only a partial view of a project).

note: it may be simpler to copy an already existing libs folder from one of the samples found in /java/samples.

in eclipse, right-click on your project and select the properties option.

select the java build path.

click add jars.

select the alljoyn.jar file from

the /libs folder.

important: make sure the files are from the android distribution of the alljoyn framework and not the linux distribution. the files will have identical names, but the linux distribution is not compiled for the arm processor used on most android phones; it is

designed for the pc x86 architecture.

create a new java project.

in the step where you define the java build settings, select the libraries tab.

click add external jars...

select alljoyn.jar from the /java/jar

directory.

click the arrow next to the alljoyn.jar listed in your jars, which brings up a list of source attachment, javadoc location, native library location, and access rules for alljoyn.jar.

double-click native library

location, which brings up a dialog.

click the external folder... button.

select the directory that contains liballjoyn_java.so /java/lib.

double-click javadoc location.

click browse....

select the directory that contains the java docs for alljoyn.jar /java/docs.

note: to add the alljoyn framework to an already existing project, go to the project properties, select the 'java build path' properties, and follow the steps listed above.

you must load the native system library into your code at runtime. to do so, add the following lines to your code.

the alljoyn framework enables inter-process communication through an object. the object is defined as a bus interface.

each interface can contain:

methods

signals

properties

the @businterface annotation tells the code that this interface is an alljoyn interface. all bus interfaces must have a name. if you do not assign a name, a default name is assigned.

the default interface name is ..

generally, you should choose your interface name and not use the default. set the interface name by specifying the name annotation property.

interfaces have names with type string, meaning that they must be valid utf-8 characters.

there are also some additional restrictions that apply to interface names, specifically:

interface names are composed of one or more elements separated by a period (".") character. all elements must contain at least one character.

each element must only contain the ascii characters [a-z][a-z][0-9]_ and must not begin with a digit.

interface names must contain at least one "." (period) character (and thus at least two elements).

interface names must not begin with a "." (period) character.

interface names must not exceed the maximum name length of 255 characters.

the @busmethod annotation tells the java compiler that this is a bus method. alljoyn methods work almost identical to a regular method in java. the major difference is that the alljoyn methods execute on a different process or device. the method can accept

the @busmethod annotation has four properties: annotation, name, signature, and replysignature.

under normal circumstances, the values for the annotation properties can be determined by the alljoyn framework. however, there are instances in which the signature must be specified; for example you need to send an unsigned integer in a method. since java

does not have an unsigned integer type, this must be specified in the @busmethod annotation.

must specify the signature is when it cannot be obtained from the interface itself.

the @bussignal annotation specifies that the following code is an alljoyn signal. unlike methods, signals have no replies. because of this, signals always have a return type of void.

like methods, signals can take multiple arguments.

the @busproperty annotation specifies that the following code is an alljoyn property. alljoyn properties are exactly like alljoyn methods except they are specialized for get/set commands of a single value.

indicating that an interface must use a secured authentication mechanism is as simple as adding the @secure annotation to the interface.

the @secure annotation informs the bus that no method call or signal can be sent without authentication.

the server must authenticate that the client is trusted using one of the built-in authentication protocols. after authentication is completed, all messages and signals have the body of the message encrypted. only the authenticated client can read the message.

only the body of the message is encrypted. the header is left unencrypted and any bus can read the header and use it to forward the message to its destination.

the @secure annotation has the property <code>value</code> [alljoyn

3.4.0]. the 'value' property can have one of the following values:

required (default value) - methods on the interface can only be called by an authenticated peer.

off - authentication is never required.

inherit - the object inherits the security of the objects that implement the interface.

if an unknown value is specified the interface will default to inherit.

normally, <code>value</code> will not need

to be explicitly specified unless you want the value 'off' since using @secure without specifying a value is the same as specifying <code>@secure(value="required")</code>.

interfaces that omit the @secure annotation is the same as specifying <code>@secure(value="inherit")</code>.

in general, unless you are sure your interface will never need authentication, it is best to avoid using `@secure(value="off") in your interface specification.

beyond simple data types, the alljoyn framework can handle complex data types such as arrays, maps, and structs. in the case of arrays and maps, the data type can be handled by the alljoyn code with no special action. however, structs require additional annotation.

the alljoyn framework must know the order of all elements of a struct so that it can marshal and unmarshal (i.e., serialize) the message. this is where the @position annotation is used.

say that you have a structure that holds information about a photo; the code could look something like this:

the @position annotation numbering must start from "0" (zero) and count up in increments of one. skipping a number, like going from @position(1) to @position(3) without having an @position(2) anywhere in the code, is a logic error.

if, for some reason, an interface with multiple return values is used, a container must be made to hold those values. if you have a method called "timer" that returns a starttime and an endtime, an interface could be designed to handle the two return values.

note: the replysignature has to be specified, or the alljoyn framework assumes the replysignature is a struct with the replysignature "(ii)".

parentheses indicate a struct, which is a single return value, while no parentheses indicate two separate return values.

when connecting with the bus, a program may act as a service, a client, or both.

connecting a service consists of several steps.

create a new busattachment.

register a busobject with a given absolute path using the busattachment.

connect the busattachment to the bus.

request a well-known name from the bus.

this code does not have any error handling; thus, it is a poor programming example. it does show what the functions are doing. the functions busattachment.registerbusobject(...), busattachment.connect(), and dbusproxyobj.requestname(...) all return status that

should be checked.

note: this code would have to be in a class that implements the busobject. if it did not implement the busobject, the following line would not work:

the flags value is a bit-mask that is used to tell the bus how to respond if the well-known name requested already exists on the bus. it also tells the bus how to act if the well-known name is already owned and another application requests the same well-known

name.

the flag options are:

busattachment.alljoyn_request_name_allow_replacement

busattachment.alljoyn_request_name_replace_existing

busattachment.alljoyn_request_name_do_not_queue

alljoyn flag name

value

alljoyn_request_name_allow_replacement

0x1

alljoyn_request_name_replace_existing

0x2

alljoyn_request_name_do_not_queue

0x4

using the value of 0x0 (zero) for the alljoyn flag is a valid option. it means do not use any requestname flags. when using no flags to request a well-known name from the bus, if the name already exists on the bus, your name request is added to the queue of

other applications that have also requested that name.

the busattachment.requestname returns one of the following status values:

dbusproxyobj.requestname result

ok

1

dbus_request_name_reply_in_queue

2

dbus_request_name_reply_exists

3

dbus_request_name_reply_already_owner

4

bus_unexpected_disposition

bus_not_connected

to connect a client:

create a busattachment.

make a proxy object with the service name, absolute service path, and the interface of interest.

make a proxy connection with the specified alljoyn interface.

(optional) register signal handlers if the interface of interest has any signals or if you are interested in any of the signals that are part of the d-bus or alljoyn specification.

mmyinterface can now be used to call methods found on the service.

the given well-known name and path can have multiple interfaces. when using <code>getproxybusobject()</code>,

list all of the interfaces you are interested in using. there could have been multiple interfaces available at the object path "/servicepath." if there was a second interface called mysecondinterface and you also wanted to make a proxy connection to that interface,

then you need to list both interfaces when calling <code>getproxybusobject()</code>.

when calling the <code>getinterface()</code> method,

you are selecting one of the possible interfaces out of the interfaces obtained from the <code>getproxybusobject()</code> method

call.

what makes the alljoyn framework exciting is the ability to discover remote buses and interact with the interfaces on those remote buses as if they are local.

the alljoyn router runs in the background. if an application asks the router to look for a remote interface, the router tries to locate the requested service. when the router is started, it is told which transport protocols to use (i.e., bluetooth, tcp/ip).

if any device can make a connection using any of the available transport protocols, its services can then be discovered and used by all of the devices that have been discovered in a dynamically-formed, peer-to-peer network.

once the well-known name has been discovered, your application can join a started communication session using a user-selected session port.

note: valid sessionport values range from 1 to 0xffff.

for a service and client to discover each other, they must agree upon three things:

a well-known bus name that will be advertised

the object path

the session port number that will be used

the discovery sequence follows.

create a new busattachment with remotemessage.receive.

register your busobject; the local implementation of your interface with the busattachment.

register a buslistener; a bus listener may not be needed, depending on your application.

connect the busattachment with the alljoyn router.

bind the session port.

as part of binding the session port, you also need to create a sessionport listener that is used to respond to joinsession requests from the client.

advertise the same well-known name.

to connect to the advertised service:

register a buslistener; in this case it is required for the foundadvertisedname signal.

find the advertised well-known name.

when the foundadvertisedname signal is received, join the session.

important: although you can advertise different names than your service's well-known name, it would result in the bus not being able to use your service. it is an error to advertise a different name than your service's own well-known name.

so far, we have talked about making an interface and we have talked about how to connect to the bus and discover a remote bus using sessions. this section shows how to write an alljoyn bus method that can be instantiated from a remote process.

suppose you have the following interface:

now that the sampleinterface has an implementation, you must use a sampleservice object when you are registering the bus object.

now that you have a proxy connection to the remote service through msampleinterface, you can just call it like you would if the code were implemented locally.

not all methods need to return a value and not all methods need input arguments.

note: remote methods cannot be called until we have obtained an interface object from the proxybusobject class. trying to call a remote method before obtaining the interface could result in your application crashing. the msampleinterface is a null object until

we have connected it to the service using the<code>proxybusobject.getinterface()</code> method.

signals, unlike methods, never return a value. client applications that are interested in receiving a signal must register for that signal with the bus. the client must implement a signal handler to respond to the signal for which it has registered.

a signalemitter is required to emit a signal. to create a signalemitter, you must have a busobject. the code above creates a busobject that is aware of the bus signals found in the sampleinterface. signals do not need to have any code added to transmit a signal,

you just inform the bus that a signal with a given interface exists.

once a signalemitter is created, an interface can be made to send the actual signals. no coding is needed to emit the signals beyond defining and using the interface.

the important part of the bussignalhandler is to get the iface name and the signal arguments correct. if these are not correct, it does not catch the emitted signal. the function name itself is not important; it could be changed from<code>public void buttonclicked(int id)</code> to <code>public void foo(int id)</code> and it would still catch the "buttonclicked" signal.

a signal is a way to send one-way acknowledgment data from one peer to another or to multiple peers. however, when sending a signal we typically need to be a part of a session. there are many use cases where using a session to send a signal would be unnecessary

overhead. for example, when you want to inform a peer, who is not in session with you, of a changed state of information. this works two ways, with a peer specifying whether he wants to receive such notifications and with the peer who is sending out this notification

to anyone who subscribed for it.

it should be noted that there are performance limitations that need to be understood:

a sessionless signal is not a replacement for using a session, it is a shortcut to help avoid logic of setting up a session to transmit small amounts of data.

each sessionless signal will replace the previous one. if the receiving side has not had a chance to pull the data, it will be lost and not transmitted to the other side.

per number 2, the design of a sessionless signal is not to be used for file transfer or frequent game play updates. the design is for static information such as device details, a player profile, or informing another

application to invite to a session or trigger an action.

all sessionless signals have an overhead of creating and tearing down a connection which can cause unnecessary network chatter if used improperly for sending frequent data.

the way to send sessionless signals is not much different than sending a regular signal. just set the flag specifying that it is a sessionless signal and set the session id used to send the signal to 0 since we are not a part of a session when we send this

out.

a sessionless signal does not need a session id - thus the name. the sample showing this simple feature is under<code>sdk/java/samples/sessionless</code>.

along the lines of the above-mentioned sample, let us assume that a client peer is sending out sessionless signals. a service peer is the one that has subscribed for these signals/notifications.

two things to know in the case of a sessionless signal is that:

it has a session id of 0.

it has the sessionless flag set to indicate that it is a sessionless signal.

this case can look like the following:

the service side, which wants to receive these sessionless signals, calls an <code>addmatch()</code> function

and passes in the rule for sessionless signals. this looks like (where "my.signal.service" below is the name of the interface emitting the sessionless signal):

that is all we need. the service side would have the signal handler for the signal sent by the client. no advertisements, no binding, or joining of sessions is necessary by any side.

alljoyn properties may be viewed as a specialized case of alljoyn methods. an alljoyn property must start with the word "get" or "set". if it starts with the word "get", it must return a single value and take no arguments. if it starts with the word "set",

it must take a single parameter and return void. the parameter can be a complex data type like an array or a structure. this code should closely resemble the code used for alljoyn methods.

note: the getall method can return multiple values into a map. each property can be accessed using the name of the property.

when an interface is marked as "secure", it indicates that all data sent via a method call or signal must be encrypted. only an authenticated client can use an interface that has been marked as secure by using the @secure annotation (see [making a secure bus

interface using the @secure annotation][make-secure-bus-interface-using-secure-annotation]).

it possible to use security and authentication with interfaces that do not have the @secure annotation. this is possible using object security.

object security allows you to force peer authentication even when the interface does not have the @secure annotation. it's possible to tell the alljoyn framework that you want authentication when you register a your busobject.

the proxybusobject should also indicate that it is using peer authentication.

for both the <code>registerbusobject</code> and <code>getproxybusobject</code> methods,

an extra boolean argument can be added to the method calls. if this is 'true', then peer authentication is used.

the programmer is responsible for making sure that both the busobject and proxybusobject are using object security. . if the busobject is created as a secure object, the proxybusobject should also be secure. if they do not agree, it is an error.

when using a secure interface, an authentication listener must be created to handle authentication events. the authentication listener provides authentication credentials for the local application and, depending on the authentication mechanism, checks the credential

of the remote application.

the alljoyn framework supports the following types of authentication mechanisms:

secure remote password (srp) anonymous key exchange

secure remote password (srp) logon (username and password)

rsa key exchange using x509 certificates

pin key exchange

ecdhe_null key exchange

ecdhe_psk key exchange

ecdhe_ecdsa key exchange

srp key exchange uses a one-time authenticating password to set up an authentication key. this key is used for all subsequent interactions between the client and server.

srp logon requires that the user give a username and password before the client and server are able to share an interface, unlike the srp key exchange where the password exchange is a one-time event. srp logon requires the username and password be supplied

each time a session is established between the client and server.

rsa key exchange uses rsa public key cryptography to establish a shared-secret passphrase. the shared-secret passphrase is used to establish a key. this key is then used for all subsequent exchanges between the given client and server using the protocol.

pin key exchange uses a one-time authenticating password to set up an authentication key. this key is used for all subsequent interactions between the client and server. pin key exchange is specifically designed for devices that have really stringent memory

constrains like mcus (micro control units). unless you are communicating with an mcu, it is recommended that you use an authentication mechanism other than pin key exchange.

ecdhe_null is an elliptic curve diffie-hellman ephemeral key exchange which is anonymous.

ecdhe_psk is an elliptic curve diffie-hellman ephemeral key exchange which uses a pre-shared key like a pin, a passphrase, or a symmetric key.

ecdhe_ecdsa is an elliptic curve diffie-hellman ephemeral key exchange which uses a key agreement authenticated with an asymmetric key, and validated with an elliptic curve digital signature algorithm (ecdsa) signature.

each type of security mechanism requires that an authentication listener be created that handles the specific type of security exchange needed. when creating the authentication listener, you specify which type of authentication mechanism you want to use. each

authentication mechanism is specified by a string.

authentication mechanism

string identifying the authentication mechanism

srp anonymous key exchange

alljoyn_srp_keyx

srp logon

alljoyn_srp_logon

rsa key exchange

alljoyn_rsa_keyx

pin key exchange alljoyn_pin_keyx

alljoyn_ecdhe_null

alljoyn_ecdhe_psk

alljoyn_ecdhe_ecdsa

the main difference between a secure application and a plain application, besides the @secure annotation of the interfaces, is the required inclusion of an authlistener. an implementation of an authlistener contains two callback methods:

requested

completed

any time a method call comes in on a secure interface, the alljoyn framework checks to see if it already knows how to decrypt the contents of the method call. if it cannot decrypt the method call, the <code>requested(...)</code> method

call is called.

the requested method call contains some or all of the following information:

peername - the well-known-name or unique name of the peer that initiated the authentication request.

a count of the number of authentication request attempts made. the count for the first authentication request starts at 1.

the user name of the user making the authentication request. a user name is not required for all forms of authentications and may be an empty string.

the specific type of authentication information being requested. depending on what type of authentication mechanism is being used, one or more of these requests may be made:

certificaterequest (rsa key exchange or ecdhe_ecdsa key exchange)

logonentryrequest (srp logon)

passwordrequest (srp key exchange, srp logon, rsa key exchange, pin key exchange, or ecdhe_psk key exchange)

privatekeyrequest (rsa key exchange or ecdhe_ecdsa key exchange)

usernamerequest (srp logon)

verifyrequest (rsa key exchange or ecdhe_ecdsa key exchange)

the <code>requested(...)</code> callback

method may be called one or more times for each authentication request. for each request, the program is expected to obtain or generate the required information. the requested information can be obtained from any source available including requesting information

from a user. if the requested information cannot be provided then returning false indicates failure.

once the authentication has finished the <code>completed(...)</code> callback

method is called. this contains:

the authentication mechanism that was just completed

the peername (well-known name or unique name)

a boolean variable indicating if the authentication attempt completed successfully.

as we talk about each authentication method, message sequence charts are provided showing method calls and callbacks used to set up the authenticated session. a legend for the message sequence chart (msc) is provided in the following figure.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: message sequence chart legend

the message sequence charts simplify all device-to-device communication by ignoring bus traffic. all alljoyn bus traffic is compressed into a single line in the message sequence charts labeled "alljoyn bus." this shows only the callbacks and method calls that

we should be interested in when setting up authentication.

srp key exchange works similarly to pairing bluetooth devices. the devices advertise that they want to talk with one another and when they discover one another a password is entered on the device. this one-time password proves that the device knows about the

bluetooth device. once this one-time pairing is completed, the bluetooth device can connect with the device at any time without repeating the pairing process.

there is a one-time password exchange.

once the authentication is completed the client and server can communicate without asking for passwords to be entered.

even after the program is closed and restarted, the authentication is still valid.

the following figure shows the sequence in which the callback methods are instantiated the first time a secure method call is made using srp key exchange. the sequence shown only happens the first time the sender tries to communicate with the receiver.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc srp key exchange

before calling connect, the <code>busattachment.registerauthlistener()</code> should

when registering the authlistener, we have identified what type of security the authlistener is responsible for handling - in this case, <code>"alljoyn_srp_keyx"</code>.

the first time that the requested callback is called, a six-digit random number is generated and set as the password. we check to see what <code>instanceof</code> authrequest

we received. since we are using srp key exchange, we are only expecting password requests. we cast the base authrequest type to a <code>passwordrequest</code> and

call the<code>setpassword</code> method

passing in a chararray of the six-digit random number that was generated. the code shown does not limit the number of times an application can try to authenticate with this code. we can see the number of times the user has tried to authenticate by looking

at the count parameter. this code only generates a new pin code at the start of an authentication attempt; as long as <code>completed</code> has

not been called, the same password is used.

each time that the <code>requested</code> callback

is called, the user is asked to provide the one-time password. the one-time password entered by the user must match the six-digit pin code that was randomly generated by the service. the user is given three attempts to provide the proper pin code. if the user

is unable to provide the pin code, the authentication attempt fails. just like the code used in the service's srpkeyxlistener, we also check to see what<code>instanceof</code> authrequest

we have received. since we are using srp key exchange, we are only expecting password requests. the base <code>authrequest</code> type

is cast to a <code>passwordrequest</code> type

and the password is set using the <code>setpassword</code> method.

unlike the service, this code limits the number of attempts the user is given to produce the proper pin code. after three failed attempts, this returns false indicating failure to complete authentication.

srp logon uses the familiar user name/password method for identity verification. this should be familiar to anyone who has used online shopping, email, or banking.

unlike srp key exchange and rsa key exchange, srp logon requires the user to enter the user name/password every time a session is established. the verification only lasts for the length of the session. if the program is shut down and then restarted, the user

is asked to re-authenticate.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc srp logon option 1

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc srp logon option 2

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc srp logon option 3

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc srp logon option 4

with srp logon, there are four possible sequence diagrams. when the sender is requested to provide a user name or password, they can provide just the user name or both the user name and password. the information is sent to the receiver and the receiver then

uses the user name to look up the password or logon. if the password was also supplied and matches the receiver's password or the computer logon, the authentication completes. if only the user name was supplied, the receiver then requests the user name and

password from the sender.

up being a large string that takes the form of <code>n:g:s:v</code> where:

n is a large prime number

g is the generator

s is the salt

v is the computed verifier

the logon gives all the information needed to compute the verifier from the user name and password. this is a strongly encrypted string that can be used to verify a user name and password but cannot be used to obtain the user name and password. because it is

when using a secure interface, an authentication listener must be created that handles authentication. the authentication listener decides if the connecting client is a trusted client and if they should be allowed to use the interface. the implementation of

the rest of the steps for connecting and advertising the service have been covered in the earlier sections and are unchanged for a secure interface.

when the <code>requested()</code> method

is called, it makes a <code>passwordrequest</code> and

a<code>logonentryrequest</code>. since

only passwords are provided, the <code>logonentryrequest</code> has

been ignored. the sample above has created a hashmap that contains two user names and two passwords. when the<code>requested()</code> callback

method is called, it contains the user name that can be used to obtain the password from the hashmap. if the password matches the password provided by the client, the authentication is completed. if a password was not provided, the client is asked to once

again provide a user name and password. if the service is unable to obtain the password from the user name, it requests the user name and password again.

the <code>requested()</code> method requests

both the <code>passwordrequest</code> and

the <code>usernamerequest</code>. here

we have asked the user to enter both user name and password. since this is sample code, we are also revealing the user name and password which normally would not be done unless it is a guest account. if the supplied user name and password match the user name

and password at the server, the authentication completes. if the user name and password do not match, the user gets up to three tries to enter the correct user name and password before the authentication fails.

rsa is an encryption method that has been successfully used for online commerce for years. it consists of the exchange of what is called a public key. the public key can be used to encrypt data but cannot be used to decrypt the data. to be able to decrypt the

data, the receiver would use their private key. if the data were encrypted using their public key, they can read the data contents but no one else can. to make private keys even more secure, they are typically encrypted using a password. to decrypt the private

key, the password must be known.

the rsa key exchange certificate chain requires three things:

a private key

a password used to encrypt the private key

an x.509 certificate (a standard public key structure)

the x.509 certificate contains the public key as well as additional information such as who issued the key and how long the key is valid.

if you have the three required elements, you can provide them to the authlistener when they are<code>requested()</code>.

if you have not generated the required elements, the code can automatically generate them for you.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc rsa key exchange option 1

for this option, both sender and receiver have all the full rsa certificate chain needed: their own private keys, certificates, and the password used for private key encryption. when the sender calls the remote method call to an unverified receiver, the sender

is asked to provide the rsa certificate chain. once the rsa certificate chain has been provided, the receiver also requests the rsa certificate chain. the receiver then sends out a verify request. this gives the receiver an opportunity to inspect the certificate

sent from the sender. the receiver can inspect the certificate and accept or reject the certificate based on information such as who issued the certificate.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc rsa key exchange option 2

for this option the sender does not have the rsa certificate chain needed for rsa authentication. the code can generate a self-signed rsa private key and x.509 certificate for the user. the user only needs to supply the password needed to encrypt the private

key. if multiple authentication requests are made, the sender reuses the private key and certificate generated. it is important that each additional request for password uses the same password. if a different password is supplied, you receive an error stating

that you are unable to decrypt the rsa private key. it is possible to find out if this is the first time the password is being generated by calling<code>authlistener.passwordrequest.isnewpassword()</code> method.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc rsa key exchange option 3

in this option, neither the sender nor the receiver have the rsa elements or they were already generated. both sides ask for their private key password. the sender and receiver can choose differing passwords since the password is only used for their own private

key which is not shared with the other device.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure 10: msc rsa key exchange option 4

with this option the receiver side generates its own private key and certificate.

when using a secure interface, an authentication listener must be created that handles connection authentication. the authentication listener decides if the connecting client is a trusted client and if they should be allowed to use the interface.

when an authentication request comes in, it contains a <code>privatekeyrequest</code>, <code>certificaterequest</code>,

and <code>passwordrequest</code>. this

code does not provide the private key or the x.509 certificate but still returns true for the certificaterequest. the code tries to see if it has already generated a private key and a self-signed certificate. if it has generated a private key and a self-signed

certificate, it asks for the password that was used to generate the key and certificate. if it has not generated a private key and self-signed certificate, it asks for a password to do so. in both instances you receive a passwordrequest. the only way to tell

if the private key and certificate have already been generated is to check if this is a request for a new password:<code>passwordrequest.isnewpassword()</code>.

it is important to remember the password, because the same password is expected on each additional authentication attempt. both the client and the service handle rsa authentication identically, and there is no difference between the service <code>authlistener</code> and

the client<code>authlistener</code>.

if you do not want the alljoyn framework generating your private key and x.509 certificate for you, then you need to provide the private key, the password used to encrypt the private key, and the x.509 certificate. for the following sample, the private key

and certificate were generated using openssl. for most systems, openssl is required to run the alljoyn framework. you might already have it installed on your system (openssl is not required for all installations of windows). many other tools are available

to create the private key and certificate; if you are familiar with those tools feel free to use them.

see openssl's documentation for creating keys and certificates:

<a target="_blank" href="http://www.openssl.org/docs/howto/keys.txt">http://www.openssl.org/docs/howto/keys.txt</a>

<a target="_blank" href="http://www.openssl.org/docs/howto/certificates.txt">http://www.openssl.org/docs/howto/certificates.txt</a>

to create a private key the following command was used:

you are asked to enter a pass phrase; the pass phrase "pass" was chosen.

this generates a private key that is 2048 bits in length. 2048 or higher is recommended for rsa keys.

the private key is then used to generate the x.509 certificate

you are asked to provide the following for the certificate:

the pass phrase for privkey.pem

country name (2 letter code)

state or province name (full name)

locality name (e.g., city)

organization name

organization unit name

common name (e.g., your name)

email address

this certificate is valid for just under 10 years from the date of its creation. inspect the contents of the certificate using openssl:

when the initial request for the password, private key, and certificate come in this time, we are providing the information.

the code above simply returns true when the <code>verifyrequest</code> is

this code obtains the certificate as part of the verifyrequest. the chain is converted to an <code>x509certificate</code>that

can be used to get the certificate path. the certificate path can be validated against a list of<code>trustedanchors</code> (not

shown in this code). all of our samples used self-signed certificates so there is no certificate path of authority. you would have to add yourself to the list of trusted anchors if this method of verification is used.

there are many things that could be checked in the <code>verifyrequest</code> other

than checking the certificate path of authority. for example, you could reject any certificate that was not valid for more than 10 days.

pin key exchange only uses a password for identity verification. authentication using pin key exchange is very similar to srp key exchange:

even after the program is closed and restarted the authentication is still valid.

the following figure shows the sequence in which the callback methods are instantiated for a pin key exchange. this message sequence chart is identical to the msc srp key exchange diagram.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc pin key exchange

when registering the authlistener, here we are reusing the same authentication listener that was created for the srp key exchange sample above. the only change needed to use pin key exchange is that, when registering the authlistener, the type of security the

authlistener is responsible for handling was changed from alljoyn_srp_keyx to alljoyn_pin_keyx.

the ecdhe_null keyexchange is purely anonymous. it does not require any input from the application.

the following figure shows the sequence in which the callback methods are instantiated for an ecdhe_null key exchange.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc ecdhe_null key exchange

ecdhe_psk key exchange uses a pre-shared secret for identity verification. authentication using psk key exchange is very similar to srp key exchange:

there is a one-time pre-shared secret exchange.

once the authentication is completed, the client and server can communicate without asking for pre-shared secret to be entered.

the following figure shows the sequence in which the callback methods are instantiated for an ecdhe_psk key exchange.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc ecdhe_psk key exchange

ecdhe_ecdsa key exchange uses elliptic curve cryptography (ecc)-based certificate for identity verification. the ecc curve currently supported is the nist p-256 curve.

if the application provides a private key and certificate chain in the privatekeyrequest and certificaterequest, then the alljoyn framework will send the certificate chain to the other peer for validation via the

verifyrequest.

once the authentication is completed, the client and server can communicate without asking for the keys.

the following figure shows the sequence in which the callback methods are instantiated for an ecdhe_ecdsa key exchange.

【Android平台】 Alljoyn学习笔记四 Android Core API参考 CORE API GUIDE - ANDROID

figure: msc ecdhe_ecdsa key exchange

the following table lists the fields included in a type of certificate used by the ecdhe_ecdsa key exchange. all fields in the certificate are network-byte-order byte arrays.

field name

data type

description

version

uint8[4]

version is 1.

ecc curve is nist p-256

external data digest algorithm is sha-256.

dsa algorithm is ecc nist p-256 dsa.

issuer

uint8[76]

the issuer field.

subject

the subject field.

validityfrom

uint8[8]

the validity period. subfield valid from. it's represented in seconds since epoch jan 1, 1970.

validityto

the validity period. subfield valid to. it's represented in seconds since epoch jan 1, 1970.

delegate

uint8

the delegate flag.

digest

uint8[32]

the digest of the external data. if the hash field is all 0s, then there is no external data. the hash algorithm and digest size is defined by the version number.

sig

uint8[72]

the signature. the dsa signature is computed over the fields from subject field to digest field by the issuer. the dsa algorithm is ecc nist p-256 dsa.

so far, we have not discussed where the keys exchanged during the authentication process are stored. if we register the authlistener using the two-parameter method, we are using the default keystore listener with its default options. this uses a default location

for storing the keystore. the name of the keystore is the same as the application name that was used when creating the new busattachment. the default location for the keystore differs depending on the operating system that is being used. the default location

chosen is a location that the user has permission to read and write from.

for windows xp, the file is in:

for windows 7, the file is in:

for linux, the file is in:

the default location and name of the keystore file can be changed by providing a third parameter. the third parameter can be used to specify the location where the keystore is written. note that the location provided as the third parameter when registering

the authlistener is appended to the location of the home folder for the os being used. the code must have read/write permissions to access that location.

this places the file in the users home directory in a folder named foo. the file is called filename.ks.

<code>c:\documents and settings\&lt;user_name&gt;\foo\filename.ks</code> (windows xp)

<code>c:\users\&lt;user_name&gt;\foo\filename.ks</code> (windows

7)

<code>/home/&lt;user_name&gt;/foo/filename.ks</code> (linux)

multiple applications can share a single keystore; however, the code needs to know that the keystore is being shared between multiple applications or it overrides changes made by other applications. to use a shared keystore you must specify that the keystore

is shared when registering the authlistener.

every application that wants to share the same keystore should point to the same keystore file and set the isshared option to true when registering an <code>authlistener</code>.

why would you want multiple applications to share a keystore file? this allows you to set up a family of applications that authenticate as one. once one of the applications has completed authentication, then all of the applications can use the same set of keys.

this means that only one authentication request is needed, even though multiple applications use a secure interface. under the alljoyn framework's default mode, authentication is done on an app-per-app basis, not as a family of applications.

the alljoyn framework has a built-in logging system for investigating alljoyn-related issues. only critical log output is displayed when using the alljoyn release mode.

to enable alljoyn debugging three things must be done:

set the os logging option (os-specific).

set the debug level for the module(s) of interest (on the router or the application).

set erdebug*

environment variable (only needed when logging to <code>stderr</code>).

these can be accessed using the following method calls:

the <code>busattachment.useoslogging</code> command

results in different behavior depending on what os the alljoyn code is running on, as shown in the following table.

os

true

false

windows

debugger trace log

stderr

windows rt

win rt trace log

win rt file log

linux

android

logcat

mac

for <code>busattachment.useoslogging(true)</code> on

windows and windows rt, see:

for <code>busattachment.useoslogging(false)</code> on

windows rt, a file named alljoyn.log will be created in the user's <code>documents</code> directory.

the alljoyn framework has a large number of modules, with new modules being added as it continues to be developed. several module names of interest are:

all - print all modules

alljoyn_java - trace log for the jni layer of the java bindings

alljoyn - trace of alljoyn method calls

alljoyn_obj - trace of alljoyn object to name service calls

ns - the tcp name service

each module can print out four debug levels:

high level = 0b0001

debug messages = 0b0010

api trace = 0b0100

data dump = 0b1000

as a general rule, you want a debug level and all the debug options below that option. for example:

1 = "high level"

3 = "high level" and "debug messages"

7 = "high level", "debug messages", and "api trace"

15 = "high level", "debug messages", "api trace", and "data dump"

the following code would print the high level log for all of the alljoyn modules and the api trace and below log for the alljoyn module:

the same could be achieved using the following code:

if your application is using a separate alljoyn router or it is using the built-in name discovery/advertisement services, the logs from this part of the alljoyn framework will not output logs unless you use the following call:

note: there is an error debug level that is always printed and cannot be turned off.

when logging to stderr, an environment variable must also be specified to obtain the debug log. this is a consequence of the alljoyn logging system being written in c++.

normally when using a c++ program the user would only need to specify the module they are interested in using environment variables. for example:

with java you will generally use:

if everything is set up as expected you should be log output as follows:

there are five columns for each log message printed out:

time stamp representing when the log was printed.

the debug level.

the alljoyn module the message came from.

which thread the module was running on.

which line of source code printed the message "|" the debug message.

when android starts a new activity, a thread is created that draws all of the user interface elements on the screen. by default, all code that is added to the activity also runs in the same thread. android always expects the user interface (ui) to be responsive.

if a function is called that takes too long to process information, the application is terminated by android simply because the ui was unresponsive.

alljoyn applications typically conform to the client/server model; the application is either providing a service or requesting something from a service. the service could be on a remote device or on the same device. there are no time guarantees when calling

an alljoyn method. for this reason, all calls to an alljoyn method or signal must be done in a thread that is independent of the main ui thread.

this could be done by creating a new thread each time you call a signal or method associated with the alljoyn framework. this can result in many threads being created including all the memory usage associated with each thread. if too many threads are created,

the application could quickly run out of memory and crash. this could easily happen if signals were being used to constantly update something like a game.

to avoid using a separate thread for each alljoyn call, we suggest you create a handler on a separate thread that queues alljoyn method and signal calls.

create an implementation of a handler that takes a looper as its initialization parameter.

now all of the alljoyn method calls and signal calls can be placed inside the switch statement found in the<code>handlemessage</code> method.

almost all programs that use the alljoyn framework need to implement a connect case and a disconnect case, as well as a case for each alljoyn method, property, and signal.

to start the bushandler, use the following code:

the /java/samples folder in the android sdk contains a large number of samples that all show how to use the bushandler.

in the alljoyn java api, there are two <code>busattachment.registerauthlistener(...)</code> methods.

one takes two arguments, and the other takes three arguments. when registering the authentication listener, the one with three arguments should be used. the third argument sets the name of the keystore. it is recommended to always set this to <code>getfilestreampath("alljoyn_keystore").getabsolutepath()</code> for

all android programs.

in android's security model, each application must request permission to access certain system resources. for example, <code>android.permission.read_contacts</code> is

required to access the contact information on an android device.

in catering to the security model of android, the alljoyn framework allows the service to impose permission requirements on alljoyn method/signal calls. if the service specifies the permission requirements on a call, the client must acquire those permissions

by declaring them in the <code>androidmanifest.xml</code> in

order to invoke the call. if a client has not added the proper permissions to the <code>androidmanifest.xml</code>,

the alljoyn method/signal call made by the client is rejected. if it is an alljoyn method call, the router sends back an error message <code>(status = er_alljoyn_access_permission_error)</code> to the caller. multiple permissions should be concatenated into a single string using a semicolon (;) as the delimiter, for example:

by using the access permission in the interface, we are preventing a client application from accessing data without also requesting the same device permission level as the service.

important: the peer permission check model only applies when the service/client are on the same device. if the service and client are on different devices, the peer permission check will have no effect. the device-to-device trust model should be based on authentication.

android's security model does not apply to other oss. if the alljoyn framework is used to synch contact and calendar data with a desktop computer, the computer is not expected to obtain permission to read and write the calendar and contacts. the application

installed on the android device that is communicating with the computer would still be required to have android permissions to access the information of interest.

if you are using alljoyn framework 2.6, the concept of bundled router is no longer present. a developer can still get all the functionality that was available with bundled daemon in alljoyn 2.3.6 but there is no requirement to add the additional library (bundle.jar)

to the android app or to use the extra calls that were explicitly needed to use bundle router. however, the function call

the alljoyn framework does what it is supposed to do; wonderfully. however, it cannot control certain things like the network setup over which it talks. a common example is wireless isolation. what is wireless isolation?

imagine yourself sitting in a coffee shop and wanting to play a game with your friend that uses the alljoyn framework. in a normal home setup you would be connected to your home access point and this would work seamlessly. in a coffee shop or public place the

network administrators set up the access points such that two peers cannot talk to each other directly. things like multicast and broadcast are disabled in the coffee shop to avoid bandwidth problems for other users - something that could happen if a young

college student were to set up his own peer-to-peer network in the coffee shop with his friends or use an application (not using the alljoyn framework) which floods the network with multicast/broadcast packets.

the philosophy of the alljoyn framework is to make it unnecessary for the developer to be aware of such network complexities and still achieve what the alljoyn framework originally delivers - easy service discovery and connectivity without having to change

anything depending on the network or platform. to overcome the problem of wireless isolation, we added in a parallel transport in the alljoyn framework that we call ice. ice makes sure that you are able to discover services/devices and connect to them even

if the access point you are connected to does not allow this to happen directly through it.

ice is not only a way to solve the wireless isolation problem, it also provides another means to detect proximity with other devices to aid in service discovery. to do this ice needs to know the list of access points around you. it does this by talking to the

android framework from your alljoyn app. for this reason you need to specify the call:

an ideal place to call this is just before you create a busattachment in your android application. the application context is required so that the internal alljoyn java framework can get a list of access points around you by talking to the android framework.

under the hood, the alljoyn framework does a lot of different things on different platforms for device discovery without making the developer aware of the complexities involved. on android, it requires that the developer of the app grant the app a set of four

permissions so that the alljoyn discovery works seamlessly.

the two permissions below are required because traditionally the alljoyn framework uses multicast for device discovery and the android framework requires any app that intends to use multicast to have these permissions.

the two permissions below are required when you do not want to worry about wireless isolation that may be present on the network to which you are connected. these two permissions let the alljoyn framework deal with it using ice. these permissions enable the

alljoyn framework to look for wireless access points around you and use this data to detect if you are in proximity to any other device.

proguard is one of the tools packaged with the android sdk. proguard is used to shrink, optimize, and obfuscate code when producing a release version of an .apk file. after using proguard, the resulting .apk is smaller and more difficult to reverse engineer.

knowing the names of methods, interfaces, annotations, and classes is important for the alljoyn framework to operate properly. if the code is passed through proguard the names will be changed, annotations will be removed and alljoyn code will no longer function

as expected. to use proguard, the proguard.cfg file must have additional options added.

below is a sample proguard.cfg file that would be needed if we wanted to use proguard with the android chat sample. the <code>proguard.cfg</code> is

based on the default file created when using an android project. in the sample<code>proguard.cfg</code> file

below, the text in the #required for alljoyn to function section is an example of what must be added.

when a signal is repetitively emitted, the signal header could end up being a significant portion of the data that is sent. this is especially true when the contents of the signal are really small.

header compression can be used to improve performance. since setting up header compression is an expensive operation, it should not be used except for repetitive signals.

an example would be a game that wants to constantly update its player's positions. the same signal would be repetitively sent with the new player position. header compression could be used to reduce the size of the signal.

when trying to transmit large amounts of data via bluetooth, discovery can significantly slow down the transfer speed. if the transmit time is a factor, it may be beneficial to stop discovery when sending the data.

it is good practice to shut off discovery when an interface has been found even when using tcp/ip. leaving discovery running could have an impact on battery life and network performance since the routing node continues to access the radio to look for remote

services.

when setting up a signalemitter, you can set it up to broadcast:

globally to clients only on the local device

globally to all clients even off device

to a session

to a single destination

as a general rule, it is best to avoid signal emitters that do not exist in the same session. this creates a large amount of network traffic. if you want the signal to be received by multiple devices, it is best to set up a multipoint session and send the signal

to all the devices in the session. this reduces the network traffic by sending the signal to destinations that have registered

原文来自官网 特此声明