Google I/O 2013 – Dynamically Configure Mobile Applications: Google Tag Manager for Mobile Apps


RUSSELL KETCHUM: All right. Well, I want to start by
thanking everyone for coming out and listening to us talk
today about creating highly dynamic apps using Google
Tag Manager. I am Russ Ketchum. I’m the lead product manager for
Google Analytics for Apps, and also Google Tag Manager. NEIL RHODES: And my name’s
Neil Rhodes, and I’m the engineering lead for
the Google Tag Manager for Mobile Apps. RUSSELL KETCHUM: So I want to
describe a problem to that you as developers all know
pretty well. Now here, just kind of a
quick show of hands. Who in here actually as is app
developer, iOS, Android? All right, so the vast
majority of you. Awesome. So this is a problem you all
know really well, and that is once you ship your app,
it’s frozen, right? I mean, it’s locked in. It’s stuck, and it’s effectively
cast in stone. And so what that means is if
you want to make even the tiniest change to your app,
you’re now stuck with this multi-step, kind of
indeterminate amount of time, how long it’s going to take
to get from point A to point D on here. Who knows how many other sub
steps there are in between. And it’s really complicated, so
first you start by needing to secure approvals, whether
or not these are internal approvals, or if you’re going
through an app store that requires approvals, then you
actually have to physically ship your new version. You have to wait for an upgrade
cycle to happen, and then even at the end of that,
you’re left with this version uncertainty, where some of
your users have upgraded, others haven’t. You’re using tools like Google
Analytics to figure out who’s using what version. And so it’s very difficult for
you as a developer to ensure that all of your users are
having kind of the consistent experience, or even the targeted
experience that you intend for them to have. And so today we’re here to
propose an alternative, and that is pretty simple. It’s make your app highly
configurable, and then use the Google Tag Manager SDK. And so when you do that, you
actually take this process, again, however many steps it is,
and you collapse it to a model where you’re in charge of
communicating your changes directly with your users. And you can be confident that
that’s going to happen within roughly 12 hours. So I want to introduce Neil,
and he’s going to actually explain to you how it works. So as Russ described, what
you’re going to do is make your application highly
configurable. What does that mean? That really means you’re going
to take all of the static configuration that you normally
do in an application via constants, and instead
make key value pairs and respond those keys. What sorts of things might
you want to configure? Well if you’re using AdMob, for
example, you might want to configure how often ads occur,
or how long they’re there, or where they occur
on the screen. If you’re communicating with a
back end server, what is the host name that you’re
connecting to? What is the path you’re using,
if you’re using HTTP? And that’s something that might
change over time that you want to be able
to configure. Gameplay, if you’ve
got a game. How much are particular things
worth in terms of play? How many lives do you get
if you kill the bad guy? If you have any URLs in your
application that refer to help or upgrades, or things like
that, what are those URLs? UI settings. What colors do you use? You’ve got to buy a button. What color is going
to work best? Is a red best? Is a blue best? And what if you change
your mind later on? What’s the text of
that button? What features might you have
that you want to only deploy at some later time? Miscellaneous. Some message of the day
that you want to display to the user. So here’s the basic idea. In a nutshell, it’s like this. Take the constants in your
application and replace them with calls to Google
Tag Manager. As an example here, we have a
timeout of 1,500 milliseconds. Is that really the
right number? You were sitting there coding,
and at the time that seemed like the right number. But what if it turns out
that should really be 1,800, or 1,200? By making a call to the
container which contains our values, we can get the timeout
in milliseconds, and that will give us the current setting
for that key. And similarly, we can have a
whole host of keys in our application and get those
values, and know that we’re going to get the latest
values for those. Here’s an example. You’re using AdMob to monetize
your application. AdMob has one of the parameters
that you use is the banner size, and they’re
a set of constants. One constant is banner,
which is, I believe, a 320×50 banner. Another is Smart Banner, which
configures it based on the size of the screen. Which one do you want to use? Well, Smart, because
it’s smart, right? It must be better. But maybe there are
circumstances where better is better, and why not hedge your
bets and actually just make a value for a key that says
the banner size to use? Get that string value, and
then convert it to the appropriate enumeration. And then it gives you the
flexibility later on of changing your mind. Similarly, do you want the ad
at the top of the screen or the bottom of the screen? Well, I don’t know. I looked, and my product manager
looked, and we decided the top was better. But maybe it’ll turn out that
the bottom might be better later on. That might monetize better. So again, hedge my bet by
having a Boolean that specifies what I want, and then
when I create my ad view, depending on the value of that
Boolean, either at the beginning of my layout
view, or at the end of my layout view. So a small amount of
code for that. Here’s an example for content. So what we’re doing here is just
creating a JSON with keys and values. And this is for content,
what our URLs are. As you know, URLs are
not always constant. We sometimes do want
to change them. This gives us the opportunity
to do that. You might have a much larger
group of key value pairs, and so can actually use a hierarchy,
as in here, where our top level hierarchy
is physics, or target. So this is an example we’re
going to see in a moment of a gain, which has a physics engine
for basically landing a lunar lander. So we have constants for the
gravity, the down acceleration per second, the fire, so that
is when we turn on our thrusters, how much acceleration
is there. Fuel. All those sorts of things which
normally you would just create as constants, but really
do affect the game play, and might make sense
to have configurable. So how does this
actually work? You create a container. We’re going to look at the
initialization that contains your SDK in a little bit. But you create it, and then
you go via web browser to Google Tag Manager and
edit the settings for your various keys. So change the values you want. And then what happens? Your application, which has been
written and uses the SDK, the SDK will actually go up and
periodically poll for new container values, where
the periodic is once every 12 hours. So it’ll go, grab that
new container, and then save it locally. As you’re making calls to get
values for your keys, it’s not going to the network
at that point. Instead, is using the saved
container, and just going and grabbing those values. So let’s look at a demo. I’ve got a lunar lander. So this is the lunar lander,
basically, that you may have all seen from one of the
Android sample apps. I’ve modified it slightly to
use the Google Tag Manager. So what we have is a Lander. I’m trying to land in
the landing pad on the bottom right. And I’m really not very
good at this. And so I always crash. There are a number of reasons. I always go too fast, but
also, that pad is very hard to land on. We ship this to the users, and
we find that they try playing it three or four times like
me and can’t win, and give up and go on. And so it doesn’t monetize
very well, and we get bad reviews. So it turns out we’d like
to make that pad bigger. How can we do that? So I’ve got here Google
Tag Manager. We have the different versions,
so I have over time published several versions of
these container values. Let’s look at the version we
have right now, and let’s look at this collection of values. So what I see here is various
values, again, for the physics, and then also this
target controls that target at the bottom. And I can change the width. Let’s say I change
it from 1.6 to 3. I don’t really know what the
units are, but I know this will be about twice
as wide, right? Which sounds good. Save this. And now that doesn’t change
it immediately. There are many things
I might want to change in my container. Various values. Once you create a version, that
will actually create a numbered version, and then
you can publish it. And now that’s live. So what that means is as lunar
lander applications check back into the Google Tag Manager on
the web, they’ll get this new version, the container. But the changes, we’ve changed
the value of 1.6 to 3. So what does that mean? That means if I wait 12 hours
here, are maybe on average, six hours, I’ll get
this new value. So– well, no, let’s not wait. So this particular version of
the application is a debug version that is checking
back a lot more often for demo purposes. And as you see here now, it has
a much wider landing pad. And so now I have a chance of
actually landing successfully. And I think I’ve done it
once out of 20 times. So I used to actually pay
quarters to play this as a kid, but as I remember,
I was better at it. OK, so that’s just a quick
example of this server side configuration. You just want to change a value
and have it reflected. What’s the code you have
to write for this? Well first, you need a small
amount of initialization. The first thing you actually
do is go to Google Tag Manager, create an account,
and create what we call a container. And you get from that
a container ID. So this distinguishes that
collection of values. We initialize the Tag Manager,
and then create a container. Or open the container, rather,
with Open Saved, passing that same container ID. What will that do? Well, it’ll spawn a separate
thread to go and try and read that saved container
from disk. Remember that it caches that
saved container on disk, and if that container’s not there,
or if it’s older than 12 hours, it’ll go out to the
network to try and read the freshest version. In any case, what’ll happen,
while it’s doing that, you go along doing your other
initialization, and then you call container future dot get,
which will return back your initialized container. Once you have that container,
you have four very simple calls. getLong, getString,
getDouble, and getBoolean. You give a key, it gives
you back the current value for that key. And it’s really that simple. One thing to keep in mind, if
there is no value defined for the key, it’s not going
to throw an exception or anything else. It’ll return a default
value of zero, false, an empty string. There’s one slight wrinkle. You ship your application,
someone downloads it, and then they go to run it without
a network connection. Well, by what I just said,
there’s no container yet. We haven’t actually downloaded
one from the network, and we can’t because there’s
no network. Do you really want to get
zeroes, and empty strings, and falses for all of your
values, right? No gravity makes the
game easier. On the other hand, a width of
zero makes it hard, so that wouldn’t be good. So as well, you embed and ship
within your application default values, in the
form of a JSON file. Or later on, we’ll
see you could actually put in a container. So you put all the default
values you want, and then these will be used only until
the very first time it successfully connects and gets
the latest container. And from then, the users will
always have the last published container version. RUSSELL KETCHUM: So up until
this point, we’ve been talking about making configuration
changes that would affect all of your users. And I think I’d argue, actually,
that in and of itself is really powerful. But at its core, Google Tag
Manager has a very robust rules engine, and when you use
it in concert with these server side configuration
macros, you’re actually able to segment your user base
and get at very targeted experiences for different
sets of your users. So you can actually serve up
different configurations based on what types of users you have,
where they are, who they are, those type of elements. And Neil’s going to show
you a little bit more. NEIL RHODES: So let’s say, for
instance, we ship your application and you find out
via bug reports from users, and also, let’s say via your
analytics crash exceptions, that you have a problem on a
particular version of our own operating system. OK? Android 2.2, you’ve got a
particular problem, and a timeout value you’re
using is too short. You normally use 3,000
milliseconds, and it turns out on this particular OS, you
really need to be using 5,000 milliseconds. So you’d like to make a change
such that on that version of the OS you get 5,000, any other
versions you get 3,000. What do you do? First thing you do
is create a rule. A rule that says I’m trying
to target Android 2.2. So as you can see here, what we
set up is that the platform is equal Android. Why do we have to
specify that? What’s the other possibility
for the platform? Guesses? iOS, yeah. So we have both an iOS
and an Android SDK. And then we look at the version
starting with 2.2, so we can handle any sub
ones as well. Then we set up a value for
our time out to be 5,000. And then the critical part is
we put the two together. So we say this particular
macro is only enabled on Android 2.2. So that will ensure that on
those devices, we’ll have a value of 5,000. We still need to make sure the
others have a 3,000, and only the others. We need to partition all of our
users, because we don’t want to be specifying more
than one value, right? We’re 3,000, but we’re also
5,000 if it’s Android 2.2, and then it’s a question of
which one to use. So we go back and we say, OK,
our timeout of 3,000 is going to be enabled always, but we
also have disabling rules. So we can actually say if it’s
the Android 2.2, then we will disable the 3,000 timeout. So your choice is either it’s
enabled for 2.2 and you have a value of 5,000, or if I
have 3,000, which is disabled for 2.2. And it’s one possibility
of what you can do. Another thing you could do,
change a value on a given date and time. As an example, let’s say you
have a feature that you want to launch on a particular
day and date. One way to do that is have a
version of your application that you deploy at that time,
but that feature is not really going to go live until users
actually go through and do their updates. Instead, what you might do is
go ahead and write that feature behind some flag, some
check for a Boolean. And then as soon as that Boolean
switches, that feature is suddenly active. So in this case we have a
particular enable whiz bang feature is true, and then we
have a schedule for when that’s going to be true. So it’ll have its default value
other than during this time period. So it allows you to do all sorts
of things, including turn on a particular feature
at a particular time. So we’ve looked at checking
things like the platform, the version, and directly
the time. What if it’s something distinct
to your application, or unique to your application? We’ve got that covered
as well. So what you can actually do is
have your application publish values, which you can then
write rules against. Let’s say, for example, in our
game here, if we fail to land three times or more, then
we want to put up some hints to the user. Now, that’s easy
to code, right? But what if that business
logic changes? What if you decide that number
shouldn’t be three, but instead should be four? Or you don’t want to put
up a hint at all? Or you want to do
something else? So what we’ve done here
is we say we have an enable hint is true. So your code is looking, saying
is enable hint true? If so, I’ll show a hint. If not, I won’t. And then we have a rule
associated with that, which is that the number of failed
attempts is greater than or equal to three. Where’s that num failed
attempts coming from? It’s coming from your code. And this is the code
you write. You register a handler that it’s
given, basically the name of the thing we’re interested
in, along with any arguments you want to pass. Your code then runs and
executes, and delivers back the results. In this case, of course, it’s
very simple and we don’t care about any arguments. What other sorts of macros are
there that are available to you out of the box? You got the screen size, the
language the user is using, app version platform,
OS version. And then as well, this backdoor
function call, where you can really do anything
you want. And then operators will string
numeric operators to deal with those values. Let’s look at a demonstration. So let’s imagine we ship this
application, and we find something odd. What we find is, and this is
somewhat contrived, I’ll admit, we find that the users,
we made this landing pad wider, right? And we find that users who are
not using English like it. How do we know that? We both look at their reviews,
and we also use Google Analytics and find that if
they’re using a lot of languages, they interact with
the app audit, and we have good modernization. But for some reason if they’re
using English, they don’t like it. And if we look at the reviews,
they say it’s too easy. So I guess it’s everyone
but me who can actually do well at that. So we want to go ahead, and with
our application already deployed in the field, change
it so that it’s harder for users when you’re running
in English than not. Let’s look at how we do that. So what I’m going to do is take
some of these values. I prefer to copy and paste
rather than type, because it’s so easy to get it wrong. And I’m going to create
a new macro. This new macro is going
to be [? pard, ?] let’s say. And we are going to make
different values for two of these things. We’re going to make different
values for the width. So instead of being three,
we’re going to take it back to– we’ll make it 1.7. And this is the gravity. And we also want to make it
somewhat more difficult for them, so I’ll make the
gravity be higher. We don’t want this for
everyone, though. We need a rule that says it’s
only true for English users. Right now we don’t have
any good rules, so let’s create a rule. And this rule is going to be,
we’re going to call it, I don’t know, is English,
let’s say, or English. And if the language
is equal to EN. So these are the two character
language codes. So this basically says if we’re
in English, it’s going to make the gravity be
55, and the width of the landing pad 1.7. Remember, I told you we also
have to make sure to deal with sort of the non-English case. So let’s go ahead and make
a macro for that. And my copy and paste doesn’t
work quite so well because I didn’t have the right
things in there. So I’m going to try and remember
what that was. I’m going to call it for
a moment, gravity. And then the other was the
target, and the width of 3.0. The key part here, though, is
I need– so every rule needs an enabling rule. When should this fire? Well, we want it
to fire always. The disabling rule
says except. So except for if it’s
in English. And I’ll need to go back
and change gravity to the right name. And then the final thing I
need to do is actually go change these values
here, right? These are values, again, they’re
always true, and I need to remove both the
width and the gravity. Again, because I need to have
this specified only really in one place for a given
configuration. At this point I can go create
a version and publish. What I’m going to do instead,
especially since I didn’t write that gravity right, is
go ahead and just publish a previous version that had these
settings already done, and where I typed it right. So I publish that, and
that’s published. So we now have got a rule for
English versus non-English. All right. So I’m in English, and in fact
what happened, that looks like about a 1.7 width, doesn’t it? At fairly high gravity. If I went to a device that had
a non-English language, it’d be different. Well, let’s just go ahead and
change the language here. So we’ll try Catalan. Not that I particularly
understand it. And we come back in and
start our game. And now we’ve got the game
that we want for the non-English friends. So this is just an example
of how the rules work. You can make these rules based,
again, on any of the predefined macros that we have,
or any settings that you want to have from your
application. Preview. So you make changes to your
container, and you want to actually try them out before
you ship them off to your thousands, or hundreds
of thousands, or millions of customers. Kind of makes sense. How do you do that? Well, in GTM, you choose
Preview and it gives you a URL. That URL, you then to email
or SMS to your device. And from your device,
you open that URL. Basically what we do is register
a scheme for you, and it opens your application. But instead of using either the
saved container or using the latest published container,
it will use the preview container you
just previewed. And you’ll then be able to
exercise your application, see this working correctly, make any
modifications you want to in the container, re-preview
until you’re happy, completely satisfied, then you hit publish,
and then your users also start getting it. Best practices. One, go ahead and
do that preview. Test it before you publish
to your wide world. A second one, read your
container values at the right time. Depending on what values you’re
looking at, you may want to read the values when you
start your application, or in a case here, we went
ahead and read as we started a new game. What we would not have wanted
done is read as we’re doing our physics simulation quality
while the user’s going. For two reasons. You want sort of your inner
loop to be as quick as possible, and we are doing
some computation. And second, imagine the user’s
experience if they’re playing the game, right? They’re carefully landing. We get a new container at the
same time, and suddenly update the gravity, and they
slam to the ground. Not so fun. So it’s up to you to determine
when makes sense. Actually call these
container values. And then finally, set the
appropriate defaults. I talked about creating
JSON default. So you can actually just
create a JSON file that contains all your keys
and the their values. As well, though, you can
actually download from Tag Manager a version of
the container. The binary file, it includes
those same keys and values, but more importantly, it
also includes rules. So you can have your default
container include rules and act differentially on different
macros and different devices as part of your
initial container. Of course, then it’ll still
always use the latest version that it gets from the server. RUSSELL KETCHUM: So with that, I
hope you could tell Neil and I are pretty fired up about GTM
for mobile apps, and it’s something that we’re
announcing today. It’s not been generally
available up until this point, and we’d like to invite all of
you to join the white list. You can go to this short URL,
fill out a quick form, and that will sign you up for the
process, and we’ll be adding you in the next few weeks. Also, on your way in or at the
back of the room, we had one sheeters that should give you
the exact same information this links on there, so you
don’t need to snap a picture of the screen if you
don’t want to. And yeah, then you can just go
ahead and get at it, and hopefully start using
GTM in your apps. And with that, I’d like
to open it up for some questions thank you. I think there’s mics in the
middle, if you guys want to head to those. MALE SPEAKER: So concerning the
rules and like the time rule, where it says something
should be applied for a certain number of days, is
that controlling when– is that being evaluated on the
device, or is that being evaluated as to when it delivers the updated container? NEIL RHODES: So that’s
a good question. Most of the rules, virtually all
the rules except the one exception you’re having there
are evaluated on the device. So there’s nothing that’s
happening on the server. That particular one is done on
the server, so it actually controls what’s coming down
in the container. We are looking to try and add a
time based one that can also run on the device. MALE SPEAKER: Right, because it
would control how sharply you could start a future. MALE SPEAKER: Exactly. Because the question is, right,
do you have to wait sort of the 12 hours for it to
trickle down, or does it happen hour and minute? RUSSELL KETCHUM: That is an
important point Neil touches on, though, because a lot of
those rules obviously could raise privacy concerns, and so
it’s critically important from our standpoint that that data
never comes up over the wire. So all those different
parameters that we’re making decisions about for those
rules, that’s all being evaluated client side. NEIL RHODES: Except
that one case. MALE SPEAKER: OK, thanks. MALE SPEAKER: Can you
define a rule based on the device model? NEIL RHODES: You can. So what we have right now is– I don’t remember, to
tell you the truth. If not, we should add it. MALE SPEAKER: It would
be very useful. Sometimes there are bugs on not
Android version specific, but device specific. NEIL RHODES: That makes tons
of sense, and I just don’t remember off the
top of my head. But if not, we should have it. RUSSELL KETCHUM: It may not
in the initial set, but it’ll be there. MALE SPEAKER: Yeah. Thank you. MALE SPEAKER: Hi. I was wondering, how does this
affect testability of the app? Is there any way I can swap out
configuration when I run uni-tests and stuff? RUSSELL KETCHUM: So that’s
a great question. There are two issues for this. So let me see what the best– the uni-testing is a good
question, and I don’t have an answer to you this minute. The other question, though, is
let’s say you have a product manager who, you write some new
version, and they want to see, what’s it going
to look like? Right? But it’s not enabled
in your container. You don’t want to go publish a
new container that will send out to the rest of the world. So we actually have the idea of
a preview mode, where you can actually set values to
override what would normally be in the container. MALE SPEAKER: OK, so that would
be something I can use in tests as well? NEIL RHODES: So what that
actually will do is launch your application in
this version. It’s also a URL. I don’t how appropriate that
would be for testing. MALE SPEAKER: OK, thanks. MALE SPEAKER: So right now it
looks like you primarily support basic data types
for your containers. What would be the plan for
expanding that to, like, custom data types, or other
types of customized ability? NEIL RHODES: What sorts of
things are you interested in? MALE SPEAKER: I didn’t really
have anything specific, but I guess just in general, if you
were to create your own classes, or if there were some
non-standard, or non-primitive types that you wanted configure
somehow through GTM. NEIL RHODES: Sure. Our answer right now
is, and we may expand this in the future. But right now the answer
is if you want. So we have strings. If you have strings, you can
do whatever you want. You could, for instance, put
JSON in a string, and you could compile that if you want
in some structured data. MALE SPEAKER: Or figure out a
way to serialize something that you could, I don’t know,
abstract into GTM, and then re-parcel it, or something. NEIL RHODES: You
could do that. We don’t have any current plans
for making that easier than serializing, de-serializing
yourself. MALE SPEAKER: Thanks. MALE SPEAKER: Is there a way to
create a rule to target a certain percentage of devices
so that I could do A/B testing, or 5% of– NEIL RHODES: So let me
tell you something. So prior to December, I was
lead of the content experiments team, where we did
experimentation for the web. And the way I got into this,
so this is not a product announcement, but the way I got
into this was I wanted to be able to do experimentation
for mobile apps, and came up with the concept of doing the
keys and values, because it made tons of sense to do
that as a way to do experimentation. So you could, for instance, say
30 percent of my users, I want to do some A/B testing,
change this value or that value, or these set of values
and that set of values. That’s how I got into this, and
it continues to be a key interest, but we don’t have any
announcements right now. MALE SPEAKER: OK, thanks. NEIL RHODES: But it does
make perfect sense. MALE SPEAKER: Hi. you mentioned that most
of the rules are evaluated client side. So if I have a lot of rules,
does this mean every time they are all sent to the device? NEIL RHODES: So this
doesn’t mean every time they’re all sent. We do have the opportunity,
we’re not doing it right now, but we have the opportunity
for sending down differential updates. So if you’ve got version 10 of
the container and version 11 of the container comes down, we
can send the difs instead of sending the entire
container. But right now, yes, that
is what’s happening. Eventually, the entire container
has to make it down. Having said that, we do have
the opportunity for some server side up optimizations,
like there’s no reason to send down rules for iOS if it’s
an Android device. We’re not doing that server
side partial evaluation at this point, though. MALE SPEAKER: Thank you. RUSSELL KETCHUM: So we’re
just about over time. Maybe we can squeeze in just
one last question, then I think we’ll have
to wrap it up. MALE SPEAKER: So you said
you have an iOS SDK. Do you have this in the wild and
in the apps yet, for iOS’ that have been through the
app store approval yet? RUSSELL KETCHUM: So actually,
there’s a piece of functionality we didn’t talk
about here, which it actually is called public preview. And so you have the opportunity
with kind of the Apple review process when
you’re going through the iTunes Connect, you can actually
provide URLs that anybody with that URL can use
to see the experience. Because we’re hypersensitive to
the issue of not wanting to empower developers to ship
hidden features, and so that’s what these public preview
URLs are intended to do. So the premise is effectively,
build the app the way you want it. And then you as the developer,
the onus is on you to kind of communicate everything that
your app does to Apple. And you can use these public
preview URLs so that their reviewers can actually see
exactly the experiences that you’re intending to release. MALE SPEAKER: OK. But you don’t know of any apps
that have gone through approval yet with this? RUSSELL KETCHUM: No. MALE SPEAKER: OK. RUSSELL KETCHUM: All right. So I think that’s it. Thanks again. One last thing, on your way out,
if you want to hit those QR codes at the back to
rate the session. NEIL RHODES: And one last last
thing is we’re available at the sandbox, at the analytic
sandbox if you have any further questions or want to
talk to us individually. Thanks very much. RUSSELL KETCHUM: Thanks,
everyone.

One Reply to “Google I/O 2013 – Dynamically Configure Mobile Applications: Google Tag Manager for Mobile Apps”

  1. Thanks for the overview.  And the "Cute Animals" sample is great for trying out the concepts you have presented.    It is great to learn what "knobs" are available for tweaking!!

    (reference:  ( […]sdkextrasgooglegoogle_play_servicessamplestagmanagercuteanimals) )

Leave a Reply

Your email address will not be published. Required fields are marked *