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