packages/experimental/
知识点:
1、需要用反射来获取 MemoryFile.getFileDescriptor方法以及设置private成员变量;
2、AIDL的使用。
Ashmem/aidl/com/mycompany/ashmem/IMemoryService.aidl
// IMemoryService.aidl
package com.mycompany.ashmem;
import android.os.ParcelFileDescriptor;
// Declare any non-default types here with import statements
interface IMemoryService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
ParcelFileDescriptor getFileDescriptor();
void setValue(int val);
}
由Ashmem/aidl/com/mycompany/ashmem/IMemoryService.aidl编译出来的 IMemoryService.java文件
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: packages/experimental/Ashmem/aidl/com/mycompany/ashmem/IMemoryService.aidl
*/
package com.mycompany.ashmem;
// Declare any non-default types here with import statements
public interface IMemoryService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.mycompany.ashmem.IMemoryService {
private static final java.lang.String DESCRIPTOR = "com.mycompany.ashmem.IMemoryService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.mycompany.ashmem.IMemoryService interface,
* generating a proxy if needed.
*/
public static com.mycompany.ashmem.IMemoryService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.mycompany.ashmem.IMemoryService))) {
return ((com.mycompany.ashmem.IMemoryService) iin);
}
return new com.mycompany.ashmem.IMemoryService.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getFileDescriptor: {
data.enforceInterface(DESCRIPTOR);
android.os.ParcelFileDescriptor _result = this.getFileDescriptor();
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_setValue: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
this.setValue(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.mycompany.ashmem.IMemoryService {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public android.os.ParcelFileDescriptor getFileDescriptor() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.ParcelFileDescriptor _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getFileDescriptor, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = android.os.ParcelFileDescriptor.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void setValue(int val) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.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_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getFileDescriptor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_setValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public android.os.ParcelFileDescriptor getFileDescriptor() throws android.os.RemoteException;
public void setValue(int val) throws android.os.RemoteException;
}
Ashmem/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mycompany.ashmem"
android:sharedUserId="android.uid.system">
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.mycompany.ashmem.Server">
<intent-filter>
<action android:name="com.mycompany.ashmem.Server"></action>
</intent-filter>
</service>
</application>
</manifest>
Ashmem/Android.mk
# Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += aidl/com/mycompany/ashmem/IMemoryService.aidl
LOCAL_PACKAGE_NAME := Ashmem
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
Ashmem/res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.mycompany.ashmem.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/value" />
<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:hint="@string/hint"/>
<Button
android:id="@+id/button_read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/read"
tools:layout_editor_absoluteX="48dp"
tools:layout_editor_absoluteY="209dp" />
<Button
android:id="@+id/button_write"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/write"
tools:layout_editor_absoluteX="48dp"
tools:layout_editor_absoluteY="274dp" />
<Button
android:id="@+id/button_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/clear"
tools:layout_editor_absoluteX="48dp"
tools:layout_editor_absoluteY="333dp" />
</LinearLayout>
Ashmem/res/values/strings.xml
<resources>
<string name="app_name">Ashmem</string>
<string name="value" />
<string name="hint">Please input a value...</string>
<string name="read">Read</string>
<string name="write">write</string>
<string name="clear">clear</string>
</resources>
Ashmem
└── src
└── com
└── mycompany
└── ashmem
├── MainActivity.java
package com.mycompany.ashmem;
import android.app.Activity;
import android.content.Intent;
import android.content.ComponentName;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.*;
import java.lang.reflect.*;
import android.os.ServiceManager;
public class MainActivity extends Activity implements View.OnClickListener {
private final static String TAG = "MainActivity";
IMemoryService memoryService = null;
MemoryFile memoryFile = null;
FileInputStream inputStream = null;
private EditText valueText = null;
private Button readButton = null;
private Button writeButton = null;
private Button clearButton = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IMemoryService ms = getMemoryService();
if (ms == null) {
Log.i(TAG, "start server");
Intent intents = new Intent();
intents.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intents.setClassName("com.mycompany.ashmem", "com.mycompany.ashmem.Server");
startService(intents);
} else {
Log.i(TAG, "Memory Service has started.");
}
valueText = (EditText) findViewById(R.id.editText);
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(TAG, "client activity cretaed.");
}
@Override
public void onClick(View v) {
int val = 0;
String text = "";
switch (v.getId()) {
case R.id.button_read:
Log.i(TAG, "button read.");
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);
Log.i(TAG, "read file " + val);
} catch (IOException e) {
Log.i(TAG, "Failed to read bytes from memory file.");
e.printStackTrace();
}
} else {
Log.e(TAG, "MemoryFile is null");
}
text = String.valueOf(val);
valueText.setText(text);
valueText.setSelection(valueText.getText().length());
break;
case R.id.button_write:
Log.i(TAG, "button write.");
text = valueText.getText().toString();
if (text.equals("")) {
Toast.makeText(this, "Please input a number!", Toast.LENGTH_SHORT).show();
break;
}
try {
val = Integer.parseInt(text);
} catch (NumberFormatException e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
break;
}
IMemoryService ms = getMemoryService();
if (ms != null) {
try {
ms.setValue(val);
} catch (RemoteException e) {
Log.e(TAG, "Failed to set value to memory service.");
e.printStackTrace();
}
} else {
Log.e(TAG, "Memory Service is null");
}
break;
case R.id.button_clear:
text = "";
valueText.setText(text);
break;
}
}
private IMemoryService getMemoryService() {
if (memoryService != null) {
Log.i(TAG, "memory service aleady started.");
return memoryService;
}
memoryService = IMemoryService.Stub.asInterface(ServiceManager.getService("AnonymousSharedMemory"));
Log.i(TAG, memoryService != null ? "Succeed to get memory service." : "Failed to get memory service.");
return memoryService;
}
/**
*
*/
public void setField(String className, Object instance, String fieldName, Object value) {
if (className == null || className.equals("")) {
throw new IllegalArgumentException("className is null");
}
if (fieldName == null || fieldName.equals("")) {
throw new IllegalArgumentException("fieldName is null");
}
try {
Class<?> c = Class.forName(className);
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(instance, value);
} catch (Exception e) {
e.printStackTrace();
}
}
private MemoryFile getMemoryFile() {
if (memoryFile != null) {
return memoryFile;
}
IMemoryService ms = getMemoryService();
if (ms != null) {
try {
ParcelFileDescriptor pfd = ms.getFileDescriptor();
if (pfd == null) {
Log.i(TAG, "Failed to get memory file descriptor.");
return null;
}
FileDescriptor fd = pfd.getFileDescriptor();
if (fd == null) {
Log.e(TAG, "Failed to get memory file descriptor.");
return null;
}
int length = 4;
memoryFile = new MemoryFile("r", length);
memoryFile.close();
try {
Method native_mmap = null;
Method[] methods = MemoryFile.class.getDeclaredMethods();
for (int i = 0; methods != null && i < methods.length; i++) {
if (methods[i].getName().equals("native_mmap")) {
Log.i(TAG, "native_mmap finded.");
native_mmap = methods[i];
setField("android.os.MemoryFile", memoryFile, "mFD", fd);
setField("android.os.MemoryFile", memoryFile, "mLength", length);
native_mmap.setAccessible(true);
long address = (long) native_mmap.invoke(null, fd, length, 0x01 | 0x02);
setField("android.os.MemoryFile", memoryFile, "mAddress", address);
}
}
} catch (IllegalAccessException e) {
Log.e(TAG, "IllegalAccessException:" + e.getMessage());
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(TAG, "InvocationTargetException:" + e.getMessage());
e.printStackTrace();
}
return memoryFile;
} catch (RemoteException e) {
Log.e(TAG, "Failed to get file descriptor from memory service.");
e.printStackTrace();
} catch (IOException e) {
Log.e(TAG, "Failed to create memory file.");
e.printStackTrace();
}
} else {
Log.e(TAG, "memory service is null");
}
return memoryFile;
}
}
Ashmem
└── src
└── com
└── mycompany
└── ashmem
├── MemoryService.java
package com.mycompany.ashmem;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
import java.io.IOException;
import java.io.*;
import java.lang.reflect.*;
/**
* Created by Administrator on 2017/4/1.
*/
public class MemoryService extends IMemoryService.Stub {
private final static String TAG = "MemoryService";
private MemoryFile file = null;
public MemoryService() {
Log.i(TAG, "MemoryService");
try {
file = new MemoryFile("Ashmem", 4);
setValue(0);
} catch (IOException e) {
Log.i(TAG, "Failed to create memory file.");
e.printStackTrace();
} catch (RemoteException e) {
Log.i(TAG, "Failed to setValue().");
e.printStackTrace();
}
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public ParcelFileDescriptor getFileDescriptor() throws RemoteException {
ParcelFileDescriptor pfd = null;
try {
Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
FileDescriptor fd = (FileDescriptor) method.invoke(file);
pfd = ParcelFileDescriptor.dup(fd);
// FileDescriptor fd = file.getFileDescriptor();
// pfd = ParcelFileDescriptor.dup(fd);
Log.i(TAG, "getFileDescriptor:" + pfd);
} catch (IOException e) {
Log.e(TAG, "IOException:" + e.getMessage());
e.printStackTrace();
} catch (NoSuchMethodException e) {
Log.e(TAG, "NoSuchMethodException:" + e.getMessage());
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(TAG, "IllegalAccessException:" + e.getMessage());
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(TAG, "InvocationTargetException:" + e.getMessage());
e.printStackTrace();
}
return pfd;
}
@Override
public void setValue(int val) throws RemoteException {
if (file == null) {
Log.e(TAG, "Memory file is null.");
return;
}
Log.i(TAG, "setValue:" + val);
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(TAG, "Set value " + val + " " + buffer[0] + buffer[1] + buffer[2] + buffer[3] + " to memory file.");
} catch (IOException e) {
Log.i(TAG, "Failed to write bytes to memory file.");
e.printStackTrace();
}
}
}
Ashmem
└── src
└── com
└── mycompany
└── ashmem
├── Server.java
package com.mycompany.ashmem; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; import android.os.ServiceManager; import java.lang.reflect.Method; /** * Created by Administrator on 2017/4/5. */ public class Server extends Service { private final static String TAG = "Server"; private MemoryService memoryService = null; @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind"); return null; } @Override public void onCreate() { Log.i(TAG, "onCreate"); memoryService = new MemoryService(); try { ServiceManager.addService("AnonymousSharedMemory", memoryService); Log.i(TAG, "Succeed to add memory service."); } catch (RuntimeException e) { Log.e(TAG, "Failed to add Memory Service."); e.printStackTrace(); } } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Log.i(TAG, "onStart"); } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy"); } }