Enhance AIDL to take an explicit id for methods

This adds an annotation to methods in AIDL of the form
"void myMethod() = 3;" to explicitly set the onTransact
id for the method.  Either all methods must have explicitly
annotated id's or none of them should be explicitly annotated.
There is error checking in the AIDL compiler
for duplicate id's and id's outside of the valid range.

Bug: 7353910
Change-Id: I868045e3f112c9a279c573cea368a621116cbf77
diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp
index 0728246..071a8d7 100644
--- a/tools/aidl/aidl.cpp
+++ b/tools/aidl/aidl.cpp
@@ -23,6 +23,12 @@
 #  define O_BINARY  0
 #endif
 
+// The following are gotten as the offset from the allowable id's between
+// android.os.IBinder.FIRST_CALL_TRANSACTION=1 and
+// android.os.IBinder.LAST_CALL_TRANSACTION=16777215
+#define MIN_USER_SET_METHOD_ID                0
+#define MAX_USER_SET_METHOD_ID                16777214
+
 using namespace std;
 
 static void
@@ -847,6 +853,72 @@
     return 0;
 }
 
+static int
+check_and_assign_method_ids(const char * filename, interface_item_type* first_item)
+{
+    // Check whether there are any methods with manually assigned id's and any that are not.
+    // Either all method id's must be manually assigned or all of them must not.
+    // Also, check for duplicates of user set id's and that the id's are within the proper bounds.
+    set<int> usedIds;
+    interface_item_type* item = first_item;
+    bool hasUnassignedIds = false;
+    bool hasAssignedIds = false;
+    while (item != NULL) {
+        if (item->item_type == METHOD_TYPE) {
+            method_type* method_item = (method_type*)item;
+            if (method_item->hasId) {
+                hasAssignedIds = true;
+                method_item->assigned_id = atoi(method_item->id.data);
+                // Ensure that the user set id is not duplicated.
+                if (usedIds.find(method_item->assigned_id) != usedIds.end()) {
+                    // We found a duplicate id, so throw an error.
+                    fprintf(stderr,
+                            "%s:%d Found duplicate method id (%d) for method: %s\n",
+                            filename, method_item->id.lineno,
+                            method_item->assigned_id, method_item->name.data);
+                    return 1;
+                }
+                // Ensure that the user set id is within the appropriate limits
+                if (method_item->assigned_id < MIN_USER_SET_METHOD_ID ||
+                        method_item->assigned_id > MAX_USER_SET_METHOD_ID) {
+                    fprintf(stderr, "%s:%d Found out of bounds id (%d) for method: %s\n",
+                            filename, method_item->id.lineno,
+                            method_item->assigned_id, method_item->name.data);
+                    fprintf(stderr, "    Value for id must be between %d and %d inclusive.\n",
+                            MIN_USER_SET_METHOD_ID, MAX_USER_SET_METHOD_ID);
+                    return 1;
+                }
+                usedIds.insert(method_item->assigned_id);
+            } else {
+                hasUnassignedIds = true;
+            }
+            if (hasAssignedIds && hasUnassignedIds) {
+                fprintf(stderr,
+                        "%s: You must either assign id's to all methods or to none of them.\n",
+                        filename);
+                return 1;
+            }
+        }
+        item = item->next;
+    }
+
+    // In the case that all methods have unassigned id's, set a unique id for them.
+    if (hasUnassignedIds) {
+        int newId = 0;
+        item = first_item;
+        while (item != NULL) {
+            if (item->item_type == METHOD_TYPE) {
+                method_type* method_item = (method_type*)item;
+                method_item->assigned_id = newId++;
+            }
+            item = item->next;
+        }
+    }
+
+    // success
+    return 0;
+}
+
 // ==========================================================
 static int
 compile_aidl(Options& options)
@@ -937,6 +1009,12 @@
     bool onlyParcelable = false;
     err |= exactly_one_interface(options.inputFileName.c_str(), mainDoc, options, &onlyParcelable);
 
+    // If this includes an interface definition, then assign method ids and validate.
+    if (!onlyParcelable) {
+        err |= check_and_assign_method_ids(options.inputFileName.c_str(),
+                ((interface_type*)mainDoc)->interface_items);
+    }
+
     // after this, there shouldn't be any more errors because of the
     // input.
     if (err != 0 || mainDoc == NULL) {
diff --git a/tools/aidl/aidl_language.h b/tools/aidl/aidl_language.h
index f203dbb..de1370c 100644
--- a/tools/aidl/aidl_language.h
+++ b/tools/aidl/aidl_language.h
@@ -57,9 +57,13 @@
     buffer_type open_paren_token;
     arg_type* args;
     buffer_type close_paren_token;
+    bool hasId;
+    buffer_type equals_token;
+    buffer_type id;
     // XXX missing comments/copy text here
     buffer_type semicolon_token;
     buffer_type* comments_token; // points into this structure, DO NOT DELETE
+    int assigned_id;
 } method_type;
 
 enum {
diff --git a/tools/aidl/aidl_language_l.l b/tools/aidl/aidl_language_l.l
index 7c5290c..3d33e7a 100644
--- a/tools/aidl/aidl_language_l.l
+++ b/tools/aidl/aidl_language_l.l
@@ -36,6 +36,7 @@
 identifier  [_a-zA-Z][_a-zA-Z0-9\.]*
 whitespace  ([ \t\n\r]+)
 brackets    \[{whitespace}?\]
+idvalue     (0|[1-9][0-9]*)
 
 %%
 
@@ -77,6 +78,7 @@
 \(              { SET_BUFFER('('); return '('; }
 \)              { SET_BUFFER(')'); return ')'; }
 ,               { SET_BUFFER(','); return ','; }
+=               { SET_BUFFER('='); return '='; }
 
     /* keywords */
 parcelable      { SET_BUFFER(PARCELABLE); return PARCELABLE; }
@@ -89,7 +91,7 @@
 oneway          { SET_BUFFER(ONEWAY); return ONEWAY; }
 
 {brackets}+     { SET_BUFFER(ARRAY); return ARRAY; }
-
+{idvalue}       { SET_BUFFER(IDVALUE); return IDVALUE; }
 {identifier}                                        { SET_BUFFER(IDENTIFIER); return IDENTIFIER; }
 {identifier}\<{whitespace}*{identifier}({whitespace}*,{whitespace}*{identifier})*{whitespace}*\>    {
                                                       SET_BUFFER(GENERIC); return GENERIC; }
diff --git a/tools/aidl/aidl_language_y.y b/tools/aidl/aidl_language_y.y
index cc04d15..9b40d28 100644
--- a/tools/aidl/aidl_language_y.y
+++ b/tools/aidl/aidl_language_y.y
@@ -15,6 +15,7 @@
 %token IMPORT
 %token PACKAGE
 %token IDENTIFIER
+%token IDVALUE
 %token GENERIC
 %token ARRAY
 %token PARCELABLE
@@ -211,13 +212,16 @@
                                                         method_type *method = (method_type*)malloc(sizeof(method_type));
                                                         method->interface_item.item_type = METHOD_TYPE;
                                                         method->interface_item.next = NULL;
-                                                        method->type = $1.type;
                                                         method->oneway = false;
+                                                        method->type = $1.type;
                                                         memset(&method->oneway_token, 0, sizeof(buffer_type));
                                                         method->name = $2.buffer;
                                                         method->open_paren_token = $3.buffer;
                                                         method->args = $4.arg;
                                                         method->close_paren_token = $5.buffer;
+                                                        method->hasId = false;
+                                                        memset(&method->equals_token, 0, sizeof(buffer_type));
+                                                        memset(&method->id, 0, sizeof(buffer_type));
                                                         method->semicolon_token = $6.buffer;
                                                         method->comments_token = &method->type.type;
                                                         $$.method = method;
@@ -233,10 +237,49 @@
                                                         method->open_paren_token = $4.buffer;
                                                         method->args = $5.arg;
                                                         method->close_paren_token = $6.buffer;
+                                                        method->hasId = false;
+                                                        memset(&method->equals_token, 0, sizeof(buffer_type));
+                                                        memset(&method->id, 0, sizeof(buffer_type));
                                                         method->semicolon_token = $7.buffer;
                                                         method->comments_token = &method->oneway_token;
                                                         $$.method = method;
                                                     }
+    |    type IDENTIFIER '(' arg_list ')' '=' IDVALUE ';'  {
+                                                        method_type *method = (method_type*)malloc(sizeof(method_type));
+                                                        method->interface_item.item_type = METHOD_TYPE;
+                                                        method->interface_item.next = NULL;
+                                                        method->oneway = false;
+                                                        memset(&method->oneway_token, 0, sizeof(buffer_type));
+                                                        method->type = $1.type;
+                                                        method->name = $2.buffer;
+                                                        method->open_paren_token = $3.buffer;
+                                                        method->args = $4.arg;
+                                                        method->close_paren_token = $5.buffer;
+                                                        method->hasId = true;
+                                                        method->equals_token = $6.buffer;
+                                                        method->id = $7.buffer;
+                                                        method->semicolon_token = $8.buffer;
+                                                        method->comments_token = &method->type.type;
+                                                        $$.method = method;
+                                                    }
+    |   ONEWAY type IDENTIFIER '(' arg_list ')' '=' IDVALUE ';'  {
+                                                        method_type *method = (method_type*)malloc(sizeof(method_type));
+                                                        method->interface_item.item_type = METHOD_TYPE;
+                                                        method->interface_item.next = NULL;
+                                                        method->oneway = true;
+                                                        method->oneway_token = $1.buffer;
+                                                        method->type = $2.type;
+                                                        method->name = $3.buffer;
+                                                        method->open_paren_token = $4.buffer;
+                                                        method->args = $5.arg;
+                                                        method->close_paren_token = $6.buffer;
+                                                        method->hasId = true;
+                                                        method->equals_token = $7.buffer;
+                                                        method->id = $8.buffer;
+                                                        method->semicolon_token = $9.buffer;
+                                                        method->comments_token = &method->oneway_token;
+                                                        $$.method = method;
+                                                    }
     ;
 
 arg_list:
diff --git a/tools/aidl/generate_java_binder.cpp b/tools/aidl/generate_java_binder.cpp
index f80a388..f291ceb 100644
--- a/tools/aidl/generate_java_binder.cpp
+++ b/tools/aidl/generate_java_binder.cpp
@@ -260,7 +260,7 @@
     string transactCodeName = "TRANSACTION_";
     transactCodeName += method->name.data;
 
-    char transactCodeValue[50];
+    char transactCodeValue[60];
     sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
 
     Field* transactCode = new Field(STATIC | FINAL,
@@ -548,7 +548,8 @@
     interface_item_type* item = iface->interface_items;
     while (item != NULL) {
         if (item->item_type == METHOD_TYPE) {
-            generate_method((method_type*)item, interface, stub, proxy, index);
+            method_type * method_item = (method_type*) item;
+            generate_method(method_item, interface, stub, proxy, method_item->assigned_id);
         }
         item = item->next;
         index++;