[Android Software/Hacking General [Developers Only]] llrraa2010:
Ashmem Shared Memory Driver

Here we are going to write an example app Ashmem, including Server and Client, to show the usage of Ashmem.

frameworks/base/core/java/android/os/MemoryFile.java

public class MemoryFile

{

......

/**

* Allocates a new ashmem region. The region is initially not purgable.

*

* [MENTION=955119]param[/MENTION] name optional name for the file (can be null).

* [MENTION=955119]param[/MENTION] length of the memory file in bytes.

* [MENTION=948141]Throw[/MENTION]s IOException if the memory file could not be created.

*/

public MemoryFile(String name, int length) throws IOException {

mLength = length;

mFD = native_open(name, length);

mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);

mOwnsRegion = true;

}

/**

* Creates a reference to an existing memory file. Changes to the original file

* will be available through this reference.

* Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail.

*

* [MENTION=955119]param[/MENTION] fd File descriptor for an existing memory file, as returned by

* {@link #getFileDescriptor()}. This file descriptor will be closed

* by {@link #close()}.

* [MENTION=955119]param[/MENTION] length Length of the memory file in bytes.

* [MENTION=955119]param[/MENTION] mode File mode. Currently only "r" for read-only access is supported.

* [MENTION=948141]Throw[/MENTION]s NullPointerException if <code>fd</code> is null.

* [MENTION=948141]Throw[/MENTION]s IOException If <code>fd</code> does not refer to an existing memory file,

* or if the file mode of the existing memory file is more restrictive

* than <code>mode</code>.

*

* [MENTION=278736]hide[/MENTION]

*/

public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException {

if (fd == null) {

throw new NullPointerException("File descriptor is null.");

}

if (!isMemoryFile(fd)) {

throw new IllegalArgumentException("Not a memory file.");

}

mLength = length;

mFD = fd;

mAddress = native_mmap(mFD, length, modeToProt(mode));

mOwnsRegion = false;

}

......

}

src/shy/luo/ashmem/IMemoryService.java

package shy.luo.ashmem;

import android.util.Log;

import android.os.IInterface;

import android.os.Binder;

import android.os.IBinder;

import android.os.Parcel;

import android.os.ParcelFileDescriptor;

import android.os.RemoteException;

public interface IMemoryService extends IInterface {

public static abstract class Stub extends Binder implements IMemoryService {

private static final String DESCRIPTOR = "shy.luo.ashmem.IMemoryService";

public Stub() {

attachInterface(this, DESCRIPTOR);

}

public static IMemoryService asInterface(IBinder obj) {

if (obj == null) {

return null;

}

IInterface iin = (IInterface)obj.queryLocalInterface(DESCRIPTOR);

if (iin != null && iin instanceof IMemoryService) {

return (IMemoryService)iin;

}

return new IMemoryService.Stub.Proxy(obj);

}

public IBinder asBinder() {

return this;

}

[MENTION=439709]override[/MENTION]

public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws android.os.RemoteException {

switch (code) {

case INTERFACE_TRANSACTION: {

reply.writeString(DESCRIPTOR);

return true;

}

case TRANSACTION_getFileDescriptor: {

data.enforceInterface(DESCRIPTOR);

ParcelFileDescriptor result = this.getFileDescriptor();

reply.writeNoException();

if (result != null) {

reply.writeInt(1);

result.writeToParcel(reply, 0);

} else {

reply.writeInt(0);

}

return true;

}

case TRANSACTION_setValue: {

data.enforceInterface(DESCRIPTOR);

int val = data.readInt();

setValue(val);

reply.writeNoException();

return true;

}

}

return super.onTransact(code, data, reply, flags);

}

private static class Proxy implements IMemoryService {

private IBinder mRemote;

Proxy(IBinder remote) {

mRemote = remote;

}

public IBinder asBinder() {

return mRemote;

}

public String getInterfaceDescriptor() {

return DESCRIPTOR;

}

public ParcelFileDescriptor getFileDescriptor() throws RemoteException {

Parcel data = Parcel.obtain();

Parcel reply = Parcel.obtain();

ParcelFileDescriptor result;

try {

data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_getFileDescriptor, data, reply, 0);

reply.readException();

if (0 != reply.readInt()) {

result = ParcelFileDescriptor.CREATOR.createFromParcel(reply);

} else {

result = null;

}

} finally {

reply.recycle();

data.recycle();

}

return result;

}

public void setValue(int val) throws RemoteException {

Parcel data = Parcel.obtain();

Parcel reply = Parcel.obtain();

try {

data.writeInterfaceToken(DESCRIPTOR);

data.writeInt(val);

mRemote.transact(Stub.TRANSACTION_setValue, data, reply, 0);

reply.readException();

} finally {

reply.recycle();

data.recycle();

}

}

}

static final int TRANSACTION_getFileDescriptor = IBinder.FIRST_CALL_TRANSACTION + 0;

static final int TRANSACTION_setValue = IBinder.FIRST_CALL_TRANSACTION + 1;

}

public ParcelFileDescriptor getFileDescriptor() throws RemoteException;

public void setValue(int val) throws RemoteException;

}

src/shy/luo/ashmem/MemoryService.java

package shy.luo.ashmem;

import java.io.FileDescriptor;

import java.io.IOException;

import android.os.Parcel;

import android.os.MemoryFile;

import android.os.ParcelFileDescriptor;

import android.util.Log;

public class MemoryService extends IMemoryService.Stub {

private final static String LOG_TAG = "shy.luo.ashmem.MemoryService";

private MemoryFile file = null;

public MemoryService() {

try {

file = new MemoryFile("Ashmem", 4);

setValue(0);

}

catch(IOException ex) {

Log.i(LOG_TAG, "Failed to create memory file.");

ex.printStackTrace();

}

}

public ParcelFileDescriptor getFileDescriptor() {

Log.i(LOG_TAG, "Get File Descriptor.");

ParcelFileDescriptor pfd = null;

try {

pfd = file.getParcelFileDescriptor();

} catch(IOException ex) {

Log.i(LOG_TAG, "Failed to get file descriptor.");

ex.printStackTrace();

}

return pfd;

}

public void setValue(int val) {

if(file == null) {

return;

}

byte[] buffer = new byte[4];

buffer[0] = (byte)((val >>> 24) & 0xFF);

buffer[1] = (byte)((val >>> 16) & 0xFF);

buffer[2] = (byte)((val >>> 8) & 0xFF);

buffer[3] = (byte)(val & 0xFF);

try {

file.writeBytes(buffer, 0, 0, 4);

Log.i(LOG_TAG, "Set value " + val + " to memory file. ");

}

catch(IOException ex) {

Log.i(LOG_TAG, "Failed to write bytes to memory file.");

ex.printStackTrace();

}

}

}

src/shy/luo/ashmem/Server.java

package shy.luo.ashmem;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.util.Log;

import android.os.ServiceManager;

public class Server extends Service {

private final static String LOG_TAG = "shy.luo.ashmem.Server";

private MemoryService memoryService = null;

[MENTION=439709]override[/MENTION]

public IBinder onBind(Intent intent) {

return null;

}

[MENTION=439709]override[/MENTION]

public void onCreate() {

Log.i(LOG_TAG, "Create Memory Service...");

memoryService = new MemoryService();

try {

ServiceManager.addService("AnonymousSharedMemory", memoryService);

Log.i(LOG_TAG, "Succeed to add memory service.");

} catch (RuntimeException ex) {

Log.i(LOG_TAG, "Failed to add Memory Service.");

ex.printStackTrace();

}

}

[MENTION=439709]override[/MENTION]

public void onStart(Intent intent, int startId) {

Log.i(LOG_TAG, "Start Memory Service.");

}

[MENTION=439709]override[/MENTION]

public void onDestroy() {

Log.i(LOG_TAG, "Destroy Memory Service.");

}

}

src/shy/luo/ashmem/Client.java

package shy.luo.ashmem;

import java.io.FileDescriptor;

import java.io.IOException;

import shy.luo.ashmem.R;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.os.MemoryFile;

import android.os.ParcelFileDescriptor;

import android.os.ServiceManager;

import android.os.RemoteException;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;

public class Client extends Activity implements OnClickListener {

private final static String LOG_TAG = "shy.luo.ashmem.Client";

IMemoryService memoryService = null;

MemoryFile memoryFile = null;

private EditText valueText = null;

private Button readButton = null;

private Button writeButton = null;

private Button clearButton = null;

[MENTION=439709]override[/MENTION]

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

IMemoryService ms = getMemoryService();

if(ms == null) {

startService(new Intent("shy.luo.ashmem.server"));

} else {

Log.i(LOG_TAG, "Memory Service has started.");

}

valueText = (EditText)findViewById(R.id.edit_value);

readButton = (Button)findViewById(R.id.button_read);

writeButton = (Button)findViewById(R.id.button_write);

clearButton = (Button)findViewById(R.id.button_clear);

readButton.setOnClickListener(this);

writeButton.setOnClickListener(this);

clearButton.setOnClickListener(this);

Log.i(LOG_TAG, "Client Activity Created.");

}

[MENTION=439709]override[/MENTION]

public void onResume() {

super.onResume();

Log.i(LOG_TAG, "Client Activity Resumed.");

}

[MENTION=439709]override[/MENTION]

public void onPause() {

super.onPause();

Log.i(LOG_TAG, "Client Activity Paused.");

}

[MENTION=439709]override[/MENTION]

public void onClick(View v) {

if(v.equals(readButton)) {

int val = 0;

MemoryFile mf = getMemoryFile();

if(mf != null) {

try {

byte[] buffer = new byte[4];

mf.readBytes(buffer, 0, 0, 4);

val = (buffer[0] << 24) | ((buffer[1] & 0xFF) << 16) | ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF);

} catch(IOException ex) {

Log.i(LOG_TAG, "Failed to read bytes from memory file.");

ex.printStackTrace();

}

}

String text = String.valueOf(val);

valueText.setText(text);

} else if(v.equals(writeButton)) {

String text = valueText.getText().toString();

int val = Integer.parseInt(text);

IMemoryService ms = getMemoryService();

if(ms != null) {

try {

ms.setValue(val);

} catch(RemoteException ex) {

Log.i(LOG_TAG, "Failed to set value to memory service.");

ex.printStackTrace();

}

}

} else if(v.equals(clearButton)) {

String text = "";

valueText.setText(text);

}

}

private IMemoryService getMemoryService() {

if(memoryService != null) {

return memoryService;

}

memoryService = IMemoryService.Stub.asInterface(

ServiceManager.getService("AnonymousSharedMemory"));

Log.i(LOG_TAG, memoryService != null ? "Succeed to get memeory service." : "Failed to get memory service.");

return memoryService;

}

private MemoryFile getMemoryFile() {

if(memoryFile != null) {

return memoryFile;

}

IMemoryService ms = getMemoryService();

if(ms != null) {

try {

ParcelFileDescriptor pfd = ms.getFileDescriptor();

if(pfd == null) {

Log.i(LOG_TAG, "Failed to get memory file descriptor.");

return null;

}

try {

FileDescriptor fd = pfd.getFileDescriptor();

if(fd == null) {

Log.i(LOG_TAG, "Failed to get memeory file descriptor.");

return null;

}

memoryFile = new MemoryFile(fd, 4, "r");

} catch(IOException ex) {

Log.i(LOG_TAG, "Failed to create memory file.");

ex.printStackTrace();

}

} catch(RemoteException ex) {

Log.i(LOG_TAG, "Failed to get file descriptor from memory service.");

ex.printStackTrace();

}

}

return memoryFile;

}

}

res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<LinearLayout

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:orientation="vertical"

android:gravity="center">

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/value">

</TextView>

<EditText

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:id="@+id/edit_value"

android:hint="@string/hint">

</EditText>

</LinearLayout>

<LinearLayout

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:orientation="horizontal"

android:gravity="center">

<Button

android:id="@+id/button_read"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/read">

</Button>

<Button

android:id="@+id/button_write"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/write">

</Button>

<Button

android:id="@+id/button_clear"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/clear">

</Button>

</LinearLayout>

</LinearLayout>

res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="app_name">Ashmem</string>

<string name="value">Value</string>

<string name="hint">Please input a value...</string>

<string name="read">Read</string>

<string name="write">Write</string>

<string name="clear">Clear</string>

</resources>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="shy.luo.ashmem"

android:sharedUserId="android.uid.system"

android:versionCode="1"

android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name=".Client"

android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<service

android:enabled="true"

android:name=".Server"

android:process=".Server" >

<intent-filter>

<action android:name="shy.luo.ashmem.server"/>

<category android:name="android.intent.category.DEFAULT"/>

</intent-filter>

</service>

</application>

</manifest>

Android.mk

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES += $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := Ashmem

LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE)

build:

USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Ashmem

USER-NAME@MACHINE-NAME:~/Android$ make snod