1 /* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.hardware.location; 17 18 import android.annotation.SystemApi; 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 import android.util.Log; 22 23 import java.nio.BufferUnderflowException; 24 import java.nio.ByteBuffer; 25 import java.nio.ByteOrder; 26 import java.util.Arrays; 27 28 /** 29 * @hide 30 */ 31 @SystemApi 32 public final class NanoAppBinary implements Parcelable { 33 private static final String TAG = "NanoAppBinary"; 34 35 /* 36 * The contents of the app binary. 37 */ 38 private byte[] mNanoAppBinary; 39 40 /* 41 * Contents of the nanoapp binary header. 42 * 43 * Only valid if mHasValidHeader is true. 44 * See nano_app_binary_t in context_hub.h for details. 45 */ 46 private int mHeaderVersion; 47 private int mMagic; 48 private long mNanoAppId; 49 private int mNanoAppVersion; 50 private int mFlags; 51 private long mHwHubType; 52 private byte mTargetChreApiMajorVersion; 53 private byte mTargetChreApiMinorVersion; 54 55 private boolean mHasValidHeader = false; 56 57 /* 58 * The header version used to parse the binary in parseBinaryHeader(). 59 */ 60 private static final int EXPECTED_HEADER_VERSION = 1; 61 62 /* 63 * The magic value expected in the header as defined in context_hub.h. 64 */ 65 private static final int EXPECTED_MAGIC_VALUE = 66 (((int) 'N' << 0) | ((int) 'A' << 8) | ((int) 'N' << 16) | ((int) 'O' << 24)); 67 68 /* 69 * Byte order established in context_hub.h 70 */ 71 private static final ByteOrder HEADER_ORDER = ByteOrder.LITTLE_ENDIAN; 72 73 /* 74 * The size of the header in bytes as defined in context_hub.h. 75 */ 76 private static final int HEADER_SIZE_BYTES = 40; 77 78 /* 79 * The bit fields for mFlags as defined in context_hub.h. 80 */ 81 private static final int NANOAPP_SIGNED_FLAG_BIT = 0x1; 82 private static final int NANOAPP_ENCRYPTED_FLAG_BIT = 0x2; 83 NanoAppBinary(byte[] appBinary)84 public NanoAppBinary(byte[] appBinary) { 85 mNanoAppBinary = appBinary; 86 parseBinaryHeader(); 87 } 88 89 /* 90 * Parses the binary header and populates its field using mNanoAppBinary. 91 */ parseBinaryHeader()92 private void parseBinaryHeader() { 93 ByteBuffer buf = ByteBuffer.wrap(mNanoAppBinary).order(HEADER_ORDER); 94 95 mHasValidHeader = false; 96 try { 97 mHeaderVersion = buf.getInt(); 98 if (mHeaderVersion != EXPECTED_HEADER_VERSION) { 99 Log.e(TAG, "Unexpected header version " + mHeaderVersion + " while parsing header" 100 + " (expected " + EXPECTED_HEADER_VERSION + ")"); 101 return; 102 } 103 104 mMagic = buf.getInt(); 105 mNanoAppId = buf.getLong(); 106 mNanoAppVersion = buf.getInt(); 107 mFlags = buf.getInt(); 108 mHwHubType = buf.getLong(); 109 mTargetChreApiMajorVersion = buf.get(); 110 mTargetChreApiMinorVersion = buf.get(); 111 } catch (BufferUnderflowException e) { 112 Log.e(TAG, "Not enough contents in nanoapp header"); 113 return; 114 } 115 116 if (mMagic != EXPECTED_MAGIC_VALUE) { 117 Log.e(TAG, "Unexpected magic value " + String.format("0x%08X", mMagic) 118 + "while parsing header (expected " 119 + String.format("0x%08X", EXPECTED_MAGIC_VALUE) + ")"); 120 } else { 121 mHasValidHeader = true; 122 } 123 } 124 125 /** 126 * @return the app binary byte array 127 */ getBinary()128 public byte[] getBinary() { 129 return mNanoAppBinary; 130 } 131 132 /** 133 * @return the app binary byte array without the leading header 134 * 135 * @throws IndexOutOfBoundsException if the nanoapp binary size is smaller than the header size 136 * @throws NullPointerException if the nanoapp binary is null 137 */ getBinaryNoHeader()138 public byte[] getBinaryNoHeader() { 139 if (mNanoAppBinary.length < HEADER_SIZE_BYTES) { 140 throw new IndexOutOfBoundsException("NanoAppBinary binary byte size (" 141 + mNanoAppBinary.length + ") is less than header size (" + HEADER_SIZE_BYTES + ")"); 142 } 143 144 return Arrays.copyOfRange(mNanoAppBinary, HEADER_SIZE_BYTES, mNanoAppBinary.length); 145 } 146 147 /** 148 * @return {@code true} if the header is valid, {@code false} otherwise 149 */ hasValidHeader()150 public boolean hasValidHeader() { 151 return mHasValidHeader; 152 } 153 154 /** 155 * @return the header version 156 */ getHeaderVersion()157 public int getHeaderVersion() { 158 return mHeaderVersion; 159 } 160 161 /** 162 * @return the app ID parsed from the nanoapp header 163 */ getNanoAppId()164 public long getNanoAppId() { 165 return mNanoAppId; 166 } 167 168 /** 169 * @return the app version parsed from the nanoapp header 170 */ getNanoAppVersion()171 public int getNanoAppVersion() { 172 return mNanoAppVersion; 173 } 174 175 /** 176 * @return the compile target hub type parsed from the nanoapp header 177 */ getHwHubType()178 public long getHwHubType() { 179 return mHwHubType; 180 } 181 182 /** 183 * @return the target CHRE API major version parsed from the nanoapp header 184 */ getTargetChreApiMajorVersion()185 public byte getTargetChreApiMajorVersion() { 186 return mTargetChreApiMajorVersion; 187 } 188 189 /** 190 * @return the target CHRE API minor version parsed from the nanoapp header 191 */ getTargetChreApiMinorVersion()192 public byte getTargetChreApiMinorVersion() { 193 return mTargetChreApiMinorVersion; 194 } 195 196 /** 197 * Returns the flags for the nanoapp as defined in context_hub.h. 198 * 199 * This method is meant to be used by the Context Hub Service. 200 * 201 * @return the flags for the nanoapp 202 */ getFlags()203 public int getFlags() { 204 return mFlags; 205 } 206 207 /** 208 * @return {@code true} if the nanoapp binary is signed, {@code false} otherwise 209 */ isSigned()210 public boolean isSigned() { 211 return (mFlags & NANOAPP_SIGNED_FLAG_BIT) != 0; 212 } 213 214 /** 215 * @return {@code true} if the nanoapp binary is encrypted, {@code false} otherwise 216 */ isEncrypted()217 public boolean isEncrypted() { 218 return (mFlags & NANOAPP_ENCRYPTED_FLAG_BIT) != 0; 219 } 220 NanoAppBinary(Parcel in)221 private NanoAppBinary(Parcel in) { 222 int binaryLength = in.readInt(); 223 mNanoAppBinary = new byte[binaryLength]; 224 in.readByteArray(mNanoAppBinary); 225 226 parseBinaryHeader(); 227 } 228 229 @Override describeContents()230 public int describeContents() { 231 return 0; 232 } 233 234 @Override writeToParcel(Parcel out, int flags)235 public void writeToParcel(Parcel out, int flags) { 236 out.writeInt(mNanoAppBinary.length); 237 out.writeByteArray(mNanoAppBinary); 238 } 239 240 public static final @android.annotation.NonNull Creator<NanoAppBinary> CREATOR = 241 new Creator<NanoAppBinary>() { 242 @Override 243 public NanoAppBinary createFromParcel(Parcel in) { 244 return new NanoAppBinary(in); 245 } 246 247 @Override 248 public NanoAppBinary[] newArray(int size) { 249 return new NanoAppBinary[size]; 250 } 251 }; 252 } 253