Design engineers have traditionally built their own platforms for developing mobile phone software. But with 3G and new service requirements coming, the build your own approach breaks down. A better option could be implementing OO platform-based design
Mobile phone design engineers and object-oriented (OO), platform-based development are like Californians and mass transit: everyone can tell you the benefits, but few truly use it. Most wireless phone manufacturers continue to rely on the assembly of software components from preceding products to piece together new phone designs. Admittedly, such an ad hoc approach to software development has the illusion of expediency. Developers spend less time worrying about how components interact overall and more time on problems immediately at hand.
However, in actuality, software written in assembly code manner tends to produce tightly coupled components that evolve organically, as opposed to systematically, over time. As a result, the software inevitably becomes difficult to analyze and untangle. Components become too costly to identify, separate out, and reuse. Over the long term, this ad hoc approach proves far from expedient.
As software for wireless phones becomes more and more complex, wireless phone manufacturers must commit to a real change in philosophy. In the past, the ad hoc approach to software development for wireless phones was tacitly accepted; the relative simplicity and write-once-and-throw-away mentality of embedded software allowed it.
However, today wireless phone manufacturers are facing what producers of desktop software have had to deal with for years: the requirements of the software are no longer so simple. As network bandwidth increases, evolving networks like GPRS mature into 3G networks, and the elusive killer applications for wireless data finally emerge, wireless phone manufacturers must dedicate themselves to building on existing code more effectively.
In this article, we will explore the impact that an OO platform-based design approach has on the development of mobile phones. In addition, we will look at how OO design and OO design patterns help keep a platform reusable and extensible.
Developing with Platforms
Fundamentally, one of the key goals of any software platform is to create layers of abstraction. On wireless phones, by systematically building up layers of components, developers can reduce dependencies between higher-level code, like that for applications, and code tied specifically to hardware and telephony protocol stacks, which frequently change.
In general, lower-level components pervade more of a system than higher-level ones, so changes in these areas can have a large impact on a system overall. By limiting the effect of changes made in lower-level software, developers can write higher-level code that focuses on higher-level problems assured that their code will still work with little or no change from one phone to the next. As a result, the code can be reused and extended over time.
Figure 1 presents the architecture of a platform designed for mass-market wireless phones. The gray area at the bottom of the figure represents low-level software typically provided by hardware manufacturers, with which the platform code must interact. The platform itself, shown in blue, is organized into a series of layers. Common wireless phone applications such as a call manager, phone book, calendar, and call log reside in a layer for applications at the top of the architecture. Because applications reside in a high-level layer, they are abstracted from the code lower in the architecture that depends more directly on the hardware and protocol stack in use.
Of course, for abstraction to exist, there must be some parts of the platform that serve as the ultimate buffer between the platform and the outside world. This is one of the roles of the toolkit. Classes in the toolkit contain support for such low-level things as memory management, graphics primitives, and a basic file system for managing flash memory, as well as other utilities. The toolkit abstracts the hardware-specific details of the kernel, RTOS, and device driver software so that other parts of the platform can access these device-specific components using a reliable interface.
The application framework contains classes that serve as building blocks for applications. One feature that these building blocks provide is a consistent, structured way for applications to store, present, and control their data.
Model classes store data, view classes present the data in GUI components, and application classes control the data. Data can be anything from the contents of a personal phone book to locale information that allows a phone to configure itself with things like different strings, images, and formats on a locale-by-locale basis.
The application framework also contains classes that define the telephony application-programming interface (TAPI). This is the programmatic interface through which the platform performs tasks related to telephony, such as placing and receiving calls, and transmitting and receiving short message service (SMS) messages. If the underlying protocol stack provided by the hardware manufacturer does not include a TCP/IP stack for communicating with the Internet, this can be placed within the application framework layer as well.
Aside from a layered architecture, an important attribute of the platform presented in Figure 1 is its small scale. This is of primary importance for mass-market wireless phones, on which resources like memory and processor speed are tightly constrained. If the platform itself requires too many resources, too few will remain for applications that use the platform. In fact, scale is a major factor in why operating systems like Windows CE and Palm OS, platforms specifically designed for small devices, see little use on mass-market wireless phones. Even relatively small platforms like these remain better suited for higher-end phones where memory and processor speeds are not as restrictive.
Handling Telephony Protocols
In implementing the platform displayed in Figure 1, one potentially troublesome issue that must be resolved is how to deal with telephony protocols. This issue actually encompasses two problems: First, the platform must be malleable enough to support a variety of protocols, such as GSM/GPRS, CDMA, or TDMA; and second, given even a single protocol, the platform must be able to interact with different software used to support the protocol on various chipsets.
The first problem has perhaps a more apparent solution. Telephony protocols for the various networks typically differ enough in the features they offer that we can make a good case for creating separate applications for each one. This entails creating an application in the applications layer of the platform and various classes within the application framework to support it. For example, we could have a call manager application for GSM, one for CDMA, and others for additional networks that bring with them their own unique requirements.
To solve the second problem, we must make the TAPI component of the application framework adaptable to any specific manufacturer's telephony protocol stack interface. The fundamentals of this problem are not unique to wireless phone development. In fact, we can follow a well-known, OO blueprint: the adapter design pattern.
Adapter design patterns are generalized solutions to commonly occurring design problems. They are useful because many problems, although seemingly different on the surface, are actually quite similar when distilled to some fundamental level.
Figure 2 presents a class diagram for the adapter design pattern, modeled using the unified modeling language (UML). Fundamentally, the adapter design pattern solves the following problem: A client class requires the use of a service provided by a server class. The server class offers the service, but its interface does not match what the client expects.
The solution outlined by the adapter design pattern is as follows: Instead of interacting with the server class directly, the client should interact with a different class that acts as an adapter. The adapter class is derived from a special server class that defines an interface that the client can rely on. This special server class is abstract. The adapter class, being derived from the abstract server class, is responsible for implementing the interface that the abstract server class defines. It implements the interface by making the appropriate calls to the original server class, which the adapter design pattern calls the adaptee (i.e., the entity being adapted).
In the context of our TAPI problem, the application class, or model, that stores data is the class known within the adapter design pattern as the client. This is the class that is interested in a service offered by the protocol stack.
The interface defined for a specific manufacturer's protocol stack implementation is the interface that must be adapted. Since the application must interact with the protocol stack in a way that is consistent across different phones, we create an abstract plug-in class that defines the interface that the application can rely on.
Next, for each protocol stack interface that must be supported, we derive a specialized form of the plug-in. Each specialized plug-in implements the generic plug-in's interface by calling methods in the respective manufacturer's protocol stack code (see Figure 3).
The adapter design pattern fits the TAPI problem well. Whenever a new protocol stack interface for a given network must be supported, the only change required in the platform is the addition of a new plug-in. Since plug-ins have a clearly defined place in the platform, the addition is relatively clear-cut, especially considering the alternative: twisting through the logic of higher-level code in search of code fragments to adapt one by one.
The modular nature of plug-ins also facilitates the use of simple compiler directives to manage whether a plug-in should be built in or out of the platform depending on the target device. Finally, plug-ins can even be used to support desktop simulation.
In environments like Windows, Mac OS, or Linux, developers can write plug-ins that simulate the behavior of a particular protocol stack. This way, developers can debug a good part of their code on the desktop, as opposed to real hardware, using desktop tools that are more advanced.
Model-View-Controller Design Pattern
To this point, we have talked only briefly about how applications make use of the model, view, and application classes provided in the application framework of the platform. As seen above, models, views, and applications must work together to keep themselves coordinated. This collaboration, however, presents an interesting question for designers: Which one of these classes are in control? To answer this question, we can turn to another well-known design pattern as a guide: the model-view-controller design pattern.
Figure 4 presents a UML class diagram for the model-view-controller design pattern. Fundamentally, this design pattern solves the following problem: As controllers (application classes in our platform) cause changes to occur to data in models, views that are interested in the changes must be updated automatically. In addition, all models, views, and controllers must remain loosely coupled to ensure that others can be added and removed without modifications to the code of those that already exist. The solution outlined by the model-view-controller design pattern actually relies on another design pattern: the publisher-subscriber design pattern.
In the publisher-subscriber design pattern, a publisher maintains a list of subscribers interested in changes that occur in the publisher. When a change occurs, the publisher notifies each of its subscribers by calling a notification method implemented in the publisher base class. In turn, for each of the publisher's subscribers, the notification method calls an update method prescribed as part of the subscriber interface.
Each subscriber is responsible for implementing its own specialized form of the update method. After a subscriber is notified to update itself, it is the subscriber's responsibility to call back to the publisher in order to request information about the data that changed. To do this, the subscriber calls a data retrieval method defined by the publisher. Each specialized publisher is responsible for providing its own implementation of this method for subscribers to call.
In the model-view-controller design pattern, models are publishers and views and subscribers. As publishers, models store data and send notifications to views interested in changes to the data. As subscribers, views subscribe to models so that they receive notifications as changes in the model occur. Because views are subscribers, views wait until notified before redrawing themselves using the data from their respective models. (In practice, views also provide a method that developers can call to force an update immediately no matter the state of the data.)
As an example, suppose we wanted to include two types of clock components in our platform: a digital clock that displays the current time in a text format, and an analog clock that displays the time in a graphical format, with hands drawn over a clock face. Since the current time is typically regarded as a system-type attribute, this data might be controlled by a system application and stored in a system model. To ensure that the time in the system model gets updated in a regular fashion, the system application, as the control element, repeatedly sets a timer.
For each expiration of the timer, the system application updates the system model. As a result, the system model notifies each view interested in changes to the time. In this case, there is both a digital clock and analog clock view presenting the time; therefore, the system model calls the update method of both views to ensure that the change is reflected everywhere that the time appears in the user interface. Each view, in turn, calls back to the system model to get the current time, and then redraws itself, provided the view is currently visible.
The model-view-controller design pattern fits problems like this very well. For data that is dynamic or asynchronous, the architecture greatly simplifies the work that a developer must do to ensure that the various components in the system collaborate correctly. In addition, whenever new data needs to be managed, the changes required in the platform are relatively straightforward: add a model to maintain the data, add one or more views to display it, and add an application to control it.
A New Direction
In this article, we have seen that some of the key attributes present in desktop software for years can be valuable assets in software for wireless phones as well. Developers of wireless phones can achieve many of the same benefits as desktop developers by taking a platform-based approach, using object-oriented design, and applying object-oriented design patterns. The key is for wireless phone manufacturers to embrace these methodologies wholeheartedly. As software for wireless phones continues to become more complex, manufacturers should be making a choice: invest the time to start cultivating a platform of their own, or seek out an existing platform that can serve as a starting point. By applying sound methodologies already proven on the desktop, wireless phone manufacturers can be confident that their software will be reusable and extensible for years to come.
Mike Neil is a platform architect with Pixo. Before starting Pixo, Mike worked for Connectix on their Virtual PC product and at Apple on the first generation PowerPC based Macintoshes aswell as the Macintosh OS. He can be reached at mikeneil@pixo.com