Twelve, IPC manner in Android (4) --- use AIDL

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/yz_cfm/article/details/90483404

    AIDL (Android Interface Definition Language), Android Interface Definition Language. One way IPC in Android. He said before the IPC methods are Bundle, file sharing, Messenger, they have their own limitations, such as processing Messenger messages are processed one by one, if there are a large number of concurrent requests, inappropriate use of it. Meanwhile, Messenger's main role is to pass messages through the Message as the carrier, can carry Bundle type of data is transmitted, if the client wants to invoke server-side methods by Messenger in this way, will not be able to achieve this function. So this time we can use AIDL this way, it can invoke methods across the process of implementation. Although the underlying implementation Messenger also AIDL, but the system it is encapsulated, it can only be simple to complete a specific task, to facilitate our use.

    Let's look at the details of the use of AIDL.

    AIDL file supported data types:

    1. The basic data type (byte, int, short, long, char, boolean, float, double, etc.).

    2. String and CharSequence (a char value readable sequence).

    3. List: only support ArrayList, which each element must be supported AIDL.

    4. Map: only support HashMap, each element which must be supported AIDL, including key and value.

    5. Parcelable: Parcelable all objects that implement the interface.

    6. AIDL: All AIDL interface itself can also be used in AIDL file.

     Here we need to pay attention to:

    ① If AIDL file used in our custom object that implements the Parcelable interface, you must create a new file and AIDL the object of the same name, and in which it is declared Parcelable type.

    ② AIDL file if you want to use our custom object that implements the Parcelable interface, regardless of whether they are current and AIDL file located in the same package, the object must be explicitly import come.

    ③ AIDL in addition to basic data types, other types of parameters must be marked parameters: in, out, inout. Wherein in represents the input parameter type, out indicates an output parameter type, inout represents the input-output parameters. 

    ④ AIDL interface supports only method, does not support the statement static constant, which is different from the traditional interface.

    Due to the use of AIDL, AIDL file on the client and server must be exactly the same, including the package name. Therefore, in order to facilitate AIDL develop, recommend and AIDL all related classes and files all into the same package, so that when a client is when another application, you can simply copy the entire package to the client and service to engineering ends AIDL interface remains consistent.

    Use AIDL:

    eg1:

    AIDL relevant documents (in aidl directory):

Book.aidl:

package com.cfm.aidltest;
parcelable Book;  // 因为 Book 类是我们自定义的实现了 Parcelable 接口的类,而且在 AIDL 文件中使用到了,所以在这里要进行声明。

IBookManager.aidl: (AIDL Interface Type)

// IBookManager.aidl
package com.cfm.aidltest;
import com.cfm.aidltest.Book;   // 注意这里要显式 import 进来 Book 类。

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

Book.java: (Book class implements Parcelable interface)

package com.cfm.aidltest;

public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book(){
    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {

        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public String toString() {
        return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }
}

BookManagerService.java :( server implementation code)

package com.cfm.aidltest;

public class BookManagerService extends Service {

    private static final String TAG = "cfmtest";

    /**
     *  我们这里使用的是 CopyOnWriteArrayList 类,它实现了 Serializable 接口,所以能够跨进程传输,同时它也实现了 List 接口,
     *  所以它也属于 List 类型。而 AIDL 中支持 List 接口类型数据,所以我们可以使用 CopyOnWriteArrayList 类,只是虽然服务端返回
     *  的是 CopyOnWriteArrayList,但是在 Binder 底层会按照 List 的规范去访问数据并最终形成一个新的 ArrayList 传递给客户端。
     *  (这也是为什么前面说 AIDL 中支持的 List 只能是 ArrayList,因为 Binder 经过底层转换之后返回给客户端的就是 ArrayList 对象)
     *
     *  为什么使用这个类?由于 AIDL 方法是在服务端的 Binder 线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程
     *  同时访问的情形,所以我们要在 AIDL 方法中处理线程同步。而 CopyOnWriteArrayList 支持并发读/写,它的底层已经实现了线程
     *  同步。与此类似的还有 ConcurrentHashMap。
     */
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    // 当客户端请求服务端时,服务端完成的具体任务
    private IBinder mBinder = new IBookManager.Stub() {

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "服务端的 service 创建成功!");
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "ios"));
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

ClientActivity.java :( client implementation code)

package com.cfm.aidltest;

public class ClientActivity extends  AppCompatActivity{
    private static final String TAG = "cfmtest";

    // 绑定服务端服务
    private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 将服务端传过来的的 IBinder 类型参数转换成客户端 AIDL 接口类型以方便调用 AIDL 中的实现方法
            IBookManager clientManager = IBookManager.Stub.asInterface(service);

            try{
                // 在客户端中为 服务端添加一本书
                clientManager.addBook(new Book(3, "World Peace!"));

                List<Book> list = clientManager.getBookList();
                Log.d(TAG, "服务端返回给客户端的 List 类型: " + list.getClass().getCanonicalName());
                Log.d(TAG, "客户端取到的 List 内容: " + list.toString());
            }catch (RemoteException e){
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        // 绑定服务端服务
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 解绑服务
        unbindService(conn);
    }
}

AndroidManifest.xml:

...
<service
    android:name=".BookManagerService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
</service>
...  // 服务端与客户端不在一个进程中

Print Log Information:

// Server

com.cfm.aidltest:remote/cfmtest: 服务端的 service 创建成功!

// Client

com.cfm.aidltest/cfmtest: 服务端返回给客户端的 List 类型: java.util.ArrayList
com.cfm.aidltest/cfmtest: 客户端取到的 List 内容: [[bookId:1, bookName:Android], [bookId:2, bookName:ios], [bookId:3, bookName:World Peace!]]

    Can be seen, although the type of server to use List of CopyOnWriteArrayList class, but finally returned to the client is ArrayList. Also, we client calls addBook method for the service side of the book adds a single book, you can see also successfully acquired from the server to the information in this book just added.

    Following the introduction of a Java design patterns, then this design pattern, then the rich at the above Demo.

    java design patterns of the observer pattern:

    What is the observer pattern?

    To-many relationship of dependency between defined objects. When a state of the object changes, all objects that depend on it have been notified and updated automatically.

    Observer pattern configuration diagram:

    

 

    According to the above chart, we can see that the observer mode has the following roles:

    Subject : abstract topics (abstract to be an observer), abstract theme role to all observers object is stored in a container, each abstract theme can have any number of observers, abstract topics also provide add and remove observer object's interface (the attach () and detach () method).

    ConcreteSubject : specific topics (specifically the observed), about the role of the observer status into concrete objects, when changes in the internal state of a specific topic, send a notification to all registered observers.

    The Observer : abstract observer, the observer is an abstract class that defines an updated interface that allows to update their status at the time to get the topic change notification.

    ConcreteObserver : specific observer, implement the interface to update the definition of the abstract observer, when the specific subject state change notification, ready to perform their own content updates.

    The following give a simple example, my mother is a specific observer, son and daughter are specific observer, the rice well after her mother, daughter and son eating notification:

Subject (abstract to be an observer):

package com.cfm.observerpattern;

/**
* 抽象被观察者(Subject)
*/
public interface Subject {
    // 增加一个吃饭的人
    void attach(Observer observer);

    // 减少一个吃饭的人
    void detach(Observer observer);

    // 通知所有人吃饭
    void notifyEat();
}

ConcreteSubject (specifically the observed):

package com.cfm.observerpattern;

public class Mother implements Subject{

    // 管理要通知吃饭的人的容器
    private List<Observer> users = new ArrayList<>();

    @Override
    public void attach(Observer observer) {
        users.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        users.remove(observer);
    }

    @Override
    public void notifyEat() {
        System.out.println("妈妈饭做好了,在通知孩子们准备吃饭了!");
        for(Observer observer : users){
            observer.eat();
        }
    }
}

Observer (abstract observer):

package com.cfm.observerpattern;

/**
* 抽象观察者 Observer
*/
public interface Observer {
    void eat();
}

ConcreteObserver (specific observer):

package com.cfm.observerpattern;

/**
*  具体观察者(ConcreteObserver)
*/
public class Son implements Observer {

    private String name;

    Son(String name){
        this.name = name;
    }

    @Override
    public void eat() {
        System.out.println(this.name + " 收到了通知,准备开始吃饭!");
    }
}
package com.cfm.observerpattern;

/**
*  具体观察者(ConcreteObserver)
*/
public class Daughter implements Observer {
    private String name;

    Daughter(String name){
        this.name = name;
    }

    @Override
    public void eat() {
        System.out.println(this.name + " 收到了通知,准备开始吃饭了!");
    }
}

Test code:

package com.cfm.observerpattern;

public class Test {
    public static void main(String[] args) {

        // 创建一个具体被观察者(也就是妈妈,当妈妈把饭做好了之后,就通知儿子和女儿吃饭)
        Mother mother = new Mother();

        // 创建两个具体的观察者,并告诉妈妈,饭做好了要告诉我。
        Son son = new Son("cfm");
        Daughter daughter = new Daughter("ym");

        mother.attach(son);
        mother.attach(daughter);

        // 饭做好了,妈妈通知孩子孩子们可以吃饭了
        mother.notifyEat();
    }
}

Log information:

妈妈饭做好了,在通知孩子们准备吃饭了!
cfm 收到了通知,准备开始吃饭!
ym 收到了通知,准备开始吃饭了!

--------------------------------------Dividing line---------- ----------------------------

Now we begin to rich demo first aidl demand: When the server has a new book coming, it will notify each applicant a reminder of the client. (This will use the above said observer mode.)

IOnNewBookArrivedListener.aidl 1. Create a new file, and then add an interface method: onNewBookArrived (). This is equivalent to abstracted viewer Observer.java above and inside the update () method. Why AIDL interface instead of regular interface it? This is because they can not use common interface AIDL in. Note that this is the client to achieve specific interfaces, so its methods Binder thread pool to run on the client.

IOnNewBookArrivedListener.aidl:

package com.cfm.aidltest;

import com.cfm.aidltest.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book book);
}

2. 

IBookManager.aidl :( This is abstract observer)

package com.cfm.aidltest;

import com.cfm.aidltest.Book;
import com.cfm.aidltest.IOnNewBookArrivedListener;

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
    void registerListener(IOnNewBookArrivedListener listener);   // 注册观察者
    void unregisterListener(IOnNewBookArrivedListener listener); // 注销观察者
}

3. 

Book.aidl:

package com.cfm.aidltest;

parcelable Book;

Book.java:

package com.cfm.aidltest;

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book(){

    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {

        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public String toString() {
        return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }
}

4. Turn on a single thread in the service, every 5s to add a new book to the list of books and reminders notify registered clients.

BookManagerService.java:

package com.cfm.aidltest;

public class BookManagerService extends Service {

    private static final String TAG = "cfmtest";

    // 如果服务端服务被销毁,就停止往书单中加书的线程。
    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

    // 书单
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    // 存储具体观察者的容器,为什么使用 RemoteCallbackList,而不是普通的 List? 下面会具体分析。
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();

    // 具体的被观察者(ConcreteSubject),里面的方法运行在服务端的 Binder 线程池中。
    private Binder mBinderWithConcreteSubject = new IBookManager.Stub() {

        @Override
        public List<Book> getBookList() throws RemoteException {
            // 返回书单
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            // 向书单中添加新书
            mBookList.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            // 客户端(具体观察者)向服务端(具体被观察者)注册新书到来时的提醒通知,以当服务端有新书到来时通知给客户端。
            mListenerList.register(listener);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            // 客户端(具体观察者)向服务端(具体被观察者)注销新书到来时的提醒通知
            mListenerList.unregister(listener);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "服务端的 service 创建成功!");
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "ios"));

        // 开始一个每隔 5s 添加一本新书到书单的线程
        new Thread(new addNewBookRunnable()).start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinderWithConcreteSubject;
    }

    private void addNewBookAndNotifyClient(Book book) throws RemoteException {

        // 通知所有注册了提醒通知的客户端,有新书到了。
        mBookList.add(book); // 先添加一本新书,然后通知客户端有新书到了
        Log.d(TAG, "服务端添加了一本新书: " + book.toString());

        final int N = mListenerList.beginBroadcast(); // 返回服务端中注册的客户端数量
        for (int i = 0; i < N; i++) {
            IOnNewBookArrivedListener mClient = mListenerList.getBroadcastItem(i);

            if (mClient != null) {
                mClient.onNewBookArrived(book);
            }
        }
        mListenerList.finishBroadcast();
    }

    private class addNewBookRunnable implements Runnable {

        @Override
        public void run() {
            while (!mIsServiceDestoryed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);

                try {
                    addNewBookAndNotifyClient(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5. Client Interface to achieve the abstract observer (IOnNewBookArrivedListener) and server registration reminder notice, then log off on exit. Since the method of operation IOnNewBookArrivedListener interface in Binder client thread pool, so in order to facilitate UI operation, we have created a Handler to switch it to the main thread of the client's execution.

ClientActivity.java:

package com.cfm.aidltest;

public class ClientActivity extends  AppCompatActivity{

    private static final String TAG = "cfmtest";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private IBookManager mRemoteServerBookManager;

    private Handler mHandler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Book newBook = (Book) msg.obj;
                    Log.d(TAG, "客户端收到服务端的新书提醒,提醒的新书为: " + newBook.toString());
            }
            super.handleMessage(msg);
        }
    };

    private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try{
                mRemoteServerBookManager = bookManager; // 待会注销具体观察者时要用到
                List<Book> list = bookManager.getBookList();

                Log.d(TAG, "query book list: " + list.toString());
                mRemoteServerBookManager.registerListener(mConcreteObsever);
            }catch (RemoteException e){
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    // 具体的被观察者
    private IOnNewBookArrivedListener mConcreteObsever = new IOnNewBookArrivedListener.Stub() {

        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            // 由于这个方法运行在客户端的 Binder 线程池中,为了方便操作 UI。所以使用 Handler 切换到
            // 客户端的主线程中执行。
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        // 绑定远程服务
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // 注销提醒
        if(mRemoteServerBookManager != null && mRemoteServerBookManager.asBinder().isBinderAlive()){
            try {
                Log.d(TAG, "客户端注销提醒成功!");
                mRemoteServerBookManager.unregisterListener(mConcreteObsever);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(conn);
        super.onDestroy();
    }
}

AndroidManifest.xml:

...
<service
    android:name=".BookManagerService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
</service>
...

Print Log Information:

Server:

2019-05-22 21:16:47.308 21611-21611/com.cfm.aidltest:remote D/cfmtest: 服务端的 service 创建成功!
2019-05-22 21:16:52.325 21611-21635/com.cfm.aidltest:remote D/cfmtest: 服务端添加了一本新书: [bookId:3, bookName:new book#3]
2019-05-22 21:16:57.337 21611-21635/com.cfm.aidltest:remote D/cfmtest: 服务端添加了一本新书: [bookId:4, bookName:new book#4]
2019-05-22 21:17:02.346 21611-21635/com.cfm.aidltest:remote D/cfmtest: 服务端添加了一本新书: [bookId:5, bookName:new book#5]
2019-05-22 21:17:07.356 21611-21635/com.cfm.aidltest:remote D/cfmtest: 服务端添加了一本新书: [bookId:6, bookName:new book#6]

Client:

2019-05-22 21:16:47.388 21591-21591/com.cfm.aidltest D/cfmtest: query book list: [[bookId:1, bookName:Android], [bookId:2, bookName:ios]]
2019-05-22 21:16:52.335 21591-21591/com.cfm.aidltest D/cfmtest: 客户端收到服务端的新书提醒,提醒的新书为: [bookId:3, bookName:new book#3]
2019-05-22 21:16:57.345 21591-21591/com.cfm.aidltest D/cfmtest: 客户端收到服务端的新书提醒,提醒的新书为: [bookId:4, bookName:new book#4]
2019-05-22 21:17:02.354 21591-21591/com.cfm.aidltest D/cfmtest: 客户端收到服务端的新书提醒,提醒的新书为: [bookId:5, bookName:new book#5]
2019-05-22 21:17:07.363 21591-21591/com.cfm.aidltest D/cfmtest: 客户端收到服务端的新书提醒,提醒的新书为: [bookId:6, bookName:new book#6]
2019-05-22 21:17:09.490 21591-21591/com.cfm.aidltest D/cfmtest: 客户端注销提醒成功!

    Here you can see when the client exits to the server log off reminders success, and all this thanks to RemoteCallbackList class. Why use it? Because Binder will deliver to the client mConcreteObsever server into a brand new object, if the direct write-off will result in the server can not find this mConcreteObsever object to fail. Let's look at how you can use RemoteCallbackList.

    RemoteCallbackList Android System is dedicated to delete the cross-process for the Listener interface. Look at its source code:

public class RemoteCallbackList<E extends IInterface>{ ... }

    We can see RemoteCallbackList is a generic, arbitrary AIDL support management interfaces (that is achieved or inherited class IInterface interface).

ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();
IBinder key = listener.asBinder();
Callback value = new Callback(listener, cookie);

    The above is how it works, in its internal structure a Map AIDL designed to save all the callbacks, this is the Map key IBinder type, value is Callback type. Wherein Callback encapsulates real remote listener. When the client registers listener, and the listener of information it will deposit mCallbacks. Although the cross-process the same object transfer client will generate different objects on the server side, but their underlying Binder object are the same.

    RemoteCallback main role:

    1. When the client de-registered, we just iterate server all the listener, to find reconciliation registered listener that has the same server listener Binder object and it can be deleted.

    2. When the client process terminates, it can automatically remove listener client is registered.

    3. Internal RemoteCallback automatically thread synchronization function, so we use it to register reconciliation registration, do not need additional thread synchronization.

 

    Talk about the following points should be noted:

    1. Use RemoteCallback, we can not operate like List to operate it as you can see from the source code, it is fundamentally not a List type interface. Traversal RemoteCallback, must be carried out in the following manner, which beginBroadcast and finishBroadcast must be paired, even if we just want to get the number of elements in the RemoteCallback have to do so:

RemoteCallback mListenerList;

final int N = mListenerList.beginBroadcast(); 
for (int i = 0; i < N; i++) {
    ...
}
mListenerList.finishBroadcast();

    2. When using AIDL cross-process communication, we note that the method of the server when the client calls, because it is in the UI thread, and the thread is currently waiting for service will be suspended until the end of the method after the implementation of wake it up, so in order to avoid ANR, usually open a child thread when you call the server method.

 

    When the server process stops unexpectedly Binder cause of death, we reconnected two ways to service the client:

    1. linkToDeath () and unlinkToDeath () method, when the server Binder death, the client callback method binderDied DeathRecipient interface, then we can re-connect to the remote service in this method.

    2. In the client onServiceDisconnected () method to re-connect to the remote service.

    The difference between these two methods is, onServiceDisconnected client running on the UI thread, the thread pool and binderDied Binder run on the client. So binderDied method, we can not access the UI. Other effects are the same.

 

    Finally, we look at the permissions on validation, before we said that in addition to efficient transmission performance is also characterized by greater security in the analysis of the underlying Binder. Rewrite onTransact () method we can achieve AIDL interfaces on the server side, and verified by permission and UID / PID.

    In AndroidManifest.xml we can customize the permissions, eg:

<permission android:name="com.cfm.aidltest.ACCESS_BOOK_SERVICE"
            android:protectionLevel="normal"/>

Here we look at specific applications:

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    // 第一道: 通过 permission 验证
    int check = checkCallingOrSelfPermission("com.cfm.aidltest.ACCESS_BOOK_SERVICE");

    if( check == PackageManager.PERMISSION_DENIED){
        return false; // 权限验证失败
    }

    // 第二道: 通过 packageName 验证
    String packageName = null;

    // 通过 getCallingUid 和 getCallingPid 可以得到客户端的 Uid 和 Pid
    String[] packages = getPackageManager().getPackagesForUid(getCallingUid());

    if(packages != null && packages.length > 0){
        packageName = packages[0];
    }

    if (packageName != null && !packageName.startsWith("com.cfm")) {
        return false;
    }

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

 

Guess you like

Origin blog.csdn.net/yz_cfm/article/details/90483404