Adding a custom method to the Activity Manager service (Should know)

In this example, we will guide you through the steps needed to introduce a custom permission and learn how to make use of it to protect a method we add to the Activity Manager service.

Getting ready

We will edit the following files:

Filename

Location

ActivityManagerService.java

ANDROID_SRC/frameworks/base/services/java/com/android/server/am

ActivityManagerNative.java

ANDROID_SRC/frameworks/base/core/java/android/app

ActivityManager.java

ANDROID_SRC/frameworks/base/core/java/android/app

IActivityManager.java

ANDROID_SRC/frameworks/base/core/java/android/app

AndroidManifest.xml

ANDROID_SRC/frameworks/base/core/res

strings.xml

ANDROID_SRC/frameworks/base/core/res/res/values

How to do it...

  1. We begin by adding a new transaction and method prototype to IActivityManager.java.
  2. The following code is appended to IActivityManager.java:
    //Packt - Add this towards the end of the file
    int PACKT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 118;
    public int packtSensitiveMethod(int data) throws RemoteException;

    The number 118 is just 1 plus the last transaction number I have on my version of the source code. The ones you may see may differ. Use whatever number you see in your version and add one to it.

  3. The preceding lines basically set a transaction number for our method. Since the underlying binder driver uses these numbers to match which methods need to be performed when an IPC call is made, we set our method's transaction number to be one plus the last transaction known. Depending on the source code version you are working with, the number written above may have to be increased. The logic is simply adding 1 to the last transaction number you see. The IPC method needs to throw a RemoteException since this is mandated by the Binder IPC protocol. We then create the public interface for this method. To do this, add the following lines to ActivityManager.java, towards the end of the file:
     /* 
        Packt sensitive method demo 
        */ 
        public int packtSensitiveMethod(int data) 
        { 
          try { 
            return ActivityManagerNative.getDefault().packtSensitiveMethod(data); 
          } catch (RemoteException e) 
          { 
          } 
          return -1; 
        }
  4. This is a very simple method that does nothing useful. The aim of this recipe is to help you understand the mechanics of adding a new method to the Activity Manager service.
  5. Next, we will implement the marshalling code for this method. The concept of marshalling is no different than other interprocess frameworks. The basic aim is to serialize complex structures.
  6. Inside ActivityManagerNative.java, we make two changes. The first is inside the ActivityManagerProxy class that is inside the file. The code is organized in a way such that the ActivityManagerProxy class is towards the end of the file. Therefore, we write our proxy implementation of packtSensitiveMethod towards the end of the ActivityManagerNative.java file inside the scope of the ActivityManagerProxy class.

    The following code is added to ActivityManagerNative.java towards the end. It is the packtSensitiveMethod proxy implementation:

        //Packt 
        public int packtSensitiveMethod(int data) throws RemoteException 
        { 
          Parcel out = Parcel.obtain(); 
          Parcel reply = Parcel.obtain(); 
           
          out.writeInterfaceToken(IActivityManager.descriptor); 
          out.writeInt(data); 
           
          mRemote.transact(PACKT_TRANSACTION, out, reply, 0); 
           
          int result = reply.readInt(); 
           
          out.recycle(); 
          reply.recycle(); 
           
          return result; 
        }
  7. The second change is made in the onTransact method of the file. At the last case statement, we add our case code:
      //Packt 
            case PACKT_TRANSACTION: 
              data.enforceInterface(IActivityManager.descriptor); 
              int param = data.readInt(); 
              int result = packtSensitiveMethod(param); 
              reply.writeInt(result); 
              reply.writeNoException(); 
              return true;
  8. Finally, we have to implement this method. The following implementation is written inside ActivityManagerService.java:
    //===========================================================
    // PACKT 
    //=========================================================== 
        
        public int packtSensitiveMethod(int data) throws RemoteException 
        { 
          if(checkCallingPermission("packt.PACKT_PERMISSION") 
                           != PackageManager.PERMISSION_GRANTED) 
          { 
              throw new SecurityException("Requires permission packt.PACKT_PERMISSION"); 
            } 
            else 
            { 
            Log.i("PACKTinAMS", "sensitive method called with parameter: " + data); 
           
            return data * 2; 
          }
        }

Note

We have written the check permission code to secure this method. In the next recipe, I will guide you through the process of introducing a custom permission into the framework.

How it works...

In Android, the ActivityManagerService class executes within the context of the system server process. Hence, calls into it will be remote calls. Therefore, we have to make use of the Binder IPC mechanism. The IActivityManager.java file contains the set of remote calls exposed by the Activity Manager service. Hence, we add a new interface method packtSensitiveMethod() and a transaction constant PACKT_TRANSACTION that will correspond to this method.

ActivityManager.java represents a class library that user applications use to access the functionality of the Activity Manager service. This is similar to the custom class library we added in the earlier recipe on creating custom class libraries. As I've stated before, our implementations follow the design guidelines used in the Android code. Therefore, you will see several existing parallels in the Android source code. This is just one of the many examples that exist. Hence, we add a shim method to that file that invokes the real method through a remote procedure call.

As stated earlier, the Activity Manager service was written before the AIDL compiler existed, therefore the proxies and stubs for all remote calls are implemented manually. Hence, we add marshalling code to ActivityManagerNative.java.

You may wonder why we still add the marshalling code manually. The answer is that even though the AIDL compiler is now available, the build process was never updated for the ActivityManagerService class to make use of the AIDL compiler.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.211.87