ContextHubService: Hack around 32-bit app ID

The NanoApp.java class has a bug (b/30808791) where the API treats
app IDs as 32-bits, instead of 64-bits.  We are too late in the
Android N release cycle to change this API.

We previously put in a hack to fix this only for Google nanoapps.
However, our GTS nanoapps need this to work, and there are other
partners who need this to work in the N timeframe.  So we make
a more robust hack which parses the full 64-bit app ID out of
the header binary.

Test: Compiles and runs GTS
Bug: 31767599
Change-Id: Ic43f1f62c685fb99aac08d08767d1a67c329503f
diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java
index eea2387..06af461 100644
--- a/core/java/android/hardware/location/ContextHubService.java
+++ b/core/java/android/hardware/location/ContextHubService.java
@@ -18,6 +18,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -146,6 +148,36 @@
         return mContextHubInfo[contextHubHandle];
     }
 
+    // TODO(b/30808791): Remove this when NanoApp's API is correctly treating
+    // app IDs as 64-bits.
+    private static long parseAppId(NanoApp app) {
+        // NOTE: If this shifting seems odd (since it's actually "ONAN"), note
+        //     that it matches how this is defined in context_hub.h.
+        final int HEADER_MAGIC =
+            (((int)'N' <<  0) |
+             ((int)'A' <<  8) |
+             ((int)'N' << 16) |
+             ((int)'O' << 24));
+        final int HEADER_MAGIC_OFFSET = 4;
+        final int HEADER_APP_ID_OFFSET = 8;
+
+        ByteBuffer header = ByteBuffer.wrap(app.getAppBinary())
+            .order(ByteOrder.LITTLE_ENDIAN);
+
+        try {
+            if (header.getInt(HEADER_MAGIC_OFFSET) == HEADER_MAGIC) {
+                // This is a legitimate nanoapp header.  Let's grab the app ID.
+                return header.getLong(HEADER_APP_ID_OFFSET);
+            }
+        } catch (IndexOutOfBoundsException e) {
+            // The header is undersized.  We'll fall through to our code
+            // path below, which handles being unable to parse the header.
+        }
+        // We failed to parse the header.  Even through it's probably wrong,
+        // let's give NanoApp's idea of our ID.  This is at least consistent.
+        return app.getAppId();
+    }
+
     @Override
     public int loadNanoApp(int contextHubHandle, NanoApp app) throws RemoteException {
         checkPermissions();
@@ -162,27 +194,14 @@
         msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_LOAD_NANO_APP;
 
         long appId = app.getAppId();
-        // TODO(b/30808791): Remove this hack when the NanoApp API is fixed.
-        // Due to a bug in the NanoApp API, only the least significant four
-        // bytes of the app ID can be stored.  The most significant five
-        // bytes of a normal app ID are the "vendor", and thus the most
-        // significant of the bytes we have is the least significant byte
-        // of the vendor.  In the case that byte is the ASCII value for
-        // lower-case 'L', we assume the vendor is supposed to be "Googl"
-        // and fill in the four most significant bytes accordingly.
+        // TODO(b/30808791): Remove this hack when the NanoApp API is fixed,
+        //     and getAppId() returns a 'long' instead of an 'int'.
         if ((appId >> 32) != 0) {
             // We're unlikely to notice this warning, but at least
             // we can avoid running our hack logic.
             Log.w(TAG, "Code has not been updated since API fix.");
         } else {
-            // Note: Lower-case 'L', not the number 1.
-            if (((appId >> 24) & 0xFF) == (long)'l') {
-                // Assume we're a Google nanoapp.
-                appId |= ((long)'G') << 56;
-                appId |= ((long)'o') << 48;
-                appId |= ((long)'o') << 40;
-                appId |= ((long)'g') << 32;
-            }
+            appId = parseAppId(app);
         }
 
         msgHeader[HEADER_FIELD_LOAD_APP_ID_LO] = (int)(appId & 0xFFFFFFFF);