1 /*
2  * Copyright 2018 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.NonNull;
19 import android.annotation.Nullable;
20 import android.annotation.SystemApi;
21 import android.app.PendingIntent;
22 import android.content.Intent;
23 
24 import java.util.Objects;
25 
26 /**
27  * A helper class to retrieve information about a Intent event received for a PendingIntent
28  * registered with {@link ContextHubManager.createClient(ContextHubInfo, PendingIntent, long)}.
29  * This object can only be created through the factory method
30  * {@link ContextHubIntentEvent.fromIntent(Intent)}.
31  *
32  * @hide
33  */
34 @SystemApi
35 public class ContextHubIntentEvent {
36     @ContextHubManager.Event private final int mEventType;
37 
38     @NonNull private final ContextHubInfo mContextHubInfo;
39 
40     private final long mNanoAppId;
41 
42     private final NanoAppMessage mNanoAppMessage;
43 
44     private final int mNanoAppAbortCode;
45 
46     private final int mClientAuthorizationState;
47 
ContextHubIntentEvent( @onNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType, long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode, @ContextHubManager.AuthorizationState int clientAuthorizationState)48     private ContextHubIntentEvent(
49             @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
50             long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode,
51             @ContextHubManager.AuthorizationState int clientAuthorizationState) {
52         mContextHubInfo = contextHubInfo;
53         mEventType = eventType;
54         mNanoAppId = nanoAppId;
55         mNanoAppMessage = nanoAppMessage;
56         mNanoAppAbortCode = nanoAppAbortCode;
57         mClientAuthorizationState = clientAuthorizationState;
58     }
59 
ContextHubIntentEvent( @onNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType)60     private ContextHubIntentEvent(
61             @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType) {
62         this(contextHubInfo, eventType, -1 /* nanoAppId */, null /* nanoAppMessage */,
63                 -1 /* nanoAppAbortCode */, 0 /* clientAuthorizationState */);
64     }
65 
ContextHubIntentEvent( @onNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType, long nanoAppId)66     private ContextHubIntentEvent(
67             @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
68             long nanoAppId) {
69         this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */,
70                 -1 /* nanoAppAbortCode */, 0 /* clientAuthorizationState */);
71     }
72 
ContextHubIntentEvent( @onNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType, long nanoAppId, @NonNull NanoAppMessage nanoAppMessage)73     private ContextHubIntentEvent(
74             @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
75             long nanoAppId, @NonNull NanoAppMessage nanoAppMessage) {
76         this(contextHubInfo, eventType, nanoAppId, nanoAppMessage, -1 /* nanoAppAbortCode */,
77                 0 /* clientAuthorizationState */);
78     }
79 
ContextHubIntentEvent( @onNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType, long nanoAppId, int nanoAppAbortCode)80     private ContextHubIntentEvent(
81             @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
82             long nanoAppId, int nanoAppAbortCode) {
83         this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, nanoAppAbortCode,
84                 0 /* clientAuthorizationState */);
85     }
86 
87     /**
88      * Creates a ContextHubIntentEvent object from an Intent received through a PendingIntent
89      * registered with {@link ContextHubManager.createClient(ContextHubInfo, PendingIntent, long)}.
90      *
91      * @param intent the Intent object from an Intent event
92      * @return the ContextHubIntentEvent object describing the event
93      *
94      * @throws IllegalArgumentException if the Intent was not a valid intent
95      */
96     @NonNull
fromIntent(@onNull Intent intent)97     public static ContextHubIntentEvent fromIntent(@NonNull Intent intent) {
98         Objects.requireNonNull(intent, "Intent cannot be null");
99 
100         hasExtraOrThrow(intent, ContextHubManager.EXTRA_CONTEXT_HUB_INFO);
101         ContextHubInfo info = intent.getParcelableExtra(ContextHubManager.EXTRA_CONTEXT_HUB_INFO, android.hardware.location.ContextHubInfo.class);
102         if (info == null) {
103             throw new IllegalArgumentException("ContextHubInfo extra was null");
104         }
105 
106         int eventType = getIntExtraOrThrow(intent, ContextHubManager.EXTRA_EVENT_TYPE);
107         ContextHubIntentEvent event;
108         switch (eventType) {
109             case ContextHubManager.EVENT_NANOAPP_LOADED:
110             case ContextHubManager.EVENT_NANOAPP_UNLOADED:
111             case ContextHubManager.EVENT_NANOAPP_ENABLED:
112             case ContextHubManager.EVENT_NANOAPP_DISABLED:
113             case ContextHubManager.EVENT_NANOAPP_ABORTED:
114             case ContextHubManager.EVENT_NANOAPP_MESSAGE:
115             case ContextHubManager.EVENT_CLIENT_AUTHORIZATION: // fall through
116                 long nanoAppId = getLongExtraOrThrow(intent, ContextHubManager.EXTRA_NANOAPP_ID);
117                 if (eventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
118                     hasExtraOrThrow(intent, ContextHubManager.EXTRA_MESSAGE);
119                     NanoAppMessage message =
120                             intent.getParcelableExtra(ContextHubManager.EXTRA_MESSAGE, android.hardware.location.NanoAppMessage.class);
121                     if (message == null) {
122                         throw new IllegalArgumentException("NanoAppMessage extra was null");
123                     }
124 
125                     event = new ContextHubIntentEvent(info, eventType, nanoAppId, message);
126                 } else if (eventType == ContextHubManager.EVENT_NANOAPP_ABORTED) {
127                     int nanoAppAbortCode = getIntExtraOrThrow(
128                             intent, ContextHubManager.EXTRA_NANOAPP_ABORT_CODE);
129                     event = new ContextHubIntentEvent(info, eventType, nanoAppId, nanoAppAbortCode);
130                 } else if (eventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
131                     int authState = getIntExtraOrThrow(
132                             intent, ContextHubManager.EXTRA_CLIENT_AUTHORIZATION_STATE);
133                     event = new ContextHubIntentEvent(info, eventType, nanoAppId,
134                             null /* nanoAppMessage */, -1 /* nanoAppAbortCode */, authState);
135                 } else {
136                     event = new ContextHubIntentEvent(info, eventType, nanoAppId);
137                 }
138                 break;
139 
140             case ContextHubManager.EVENT_HUB_RESET:
141                 event = new ContextHubIntentEvent(info, eventType);
142                 break;
143 
144             default:
145                 throw new IllegalArgumentException("Unknown intent event type " + eventType);
146         }
147 
148         return event;
149     }
150 
151     /**
152      * @return the event type of this Intent event
153      */
154     @ContextHubManager.Event
getEventType()155     public int getEventType() {
156         return mEventType;
157     }
158 
159     /**
160      * @return the ContextHubInfo object describing the Context Hub this event was for
161      */
162     @NonNull
getContextHubInfo()163     public ContextHubInfo getContextHubInfo() {
164         return mContextHubInfo;
165     }
166 
167     /**
168      * @return the ID of the nanoapp this event was for
169      *
170      * @throws UnsupportedOperationException if the event did not have a nanoapp associated
171      */
getNanoAppId()172     public long getNanoAppId() {
173         if (mEventType == ContextHubManager.EVENT_HUB_RESET) {
174             throw new UnsupportedOperationException(
175                     "Cannot invoke getNanoAppId() on Context Hub reset event");
176         }
177         return mNanoAppId;
178     }
179 
180     /**
181      * @return the nanoapp's abort code
182      *
183      * @throws UnsupportedOperationException if this was not a nanoapp abort event
184      */
getNanoAppAbortCode()185     public int getNanoAppAbortCode() {
186         if (mEventType != ContextHubManager.EVENT_NANOAPP_ABORTED) {
187             throw new UnsupportedOperationException(
188                     "Cannot invoke getNanoAppAbortCode() on non-abort event: " + mEventType);
189         }
190         return mNanoAppAbortCode;
191     }
192 
193     /**
194      * @return the message from a nanoapp
195      *
196      * @throws UnsupportedOperationException if this was not a nanoapp message event
197      */
198     @NonNull
getNanoAppMessage()199     public NanoAppMessage getNanoAppMessage() {
200         if (mEventType != ContextHubManager.EVENT_NANOAPP_MESSAGE) {
201             throw new UnsupportedOperationException(
202                     "Cannot invoke getNanoAppMessage() on non-message event: " + mEventType);
203         }
204         return mNanoAppMessage;
205     }
206 
207     /**
208      * @return the client authorization state
209      *
210      * @throws UnsupportedOperationException if this was not a client authorization state event
211      */
212     @ContextHubManager.AuthorizationState
getClientAuthorizationState()213     public int getClientAuthorizationState() {
214         if (mEventType != ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
215             throw new UnsupportedOperationException(
216                     "Cannot invoke getClientAuthorizationState() on non-authorization event: "
217                     + mEventType);
218         }
219         return mClientAuthorizationState;
220     }
221 
222     @NonNull
223     @Override
toString()224     public String toString() {
225         String out = "ContextHubIntentEvent[eventType = " + mEventType
226                 + ", contextHubId = " + mContextHubInfo.getId();
227 
228         if (mEventType != ContextHubManager.EVENT_HUB_RESET) {
229             out += ", nanoAppId = 0x" + Long.toHexString(mNanoAppId);
230         }
231         if (mEventType == ContextHubManager.EVENT_NANOAPP_ABORTED) {
232             out += ", nanoAppAbortCode = " + mNanoAppAbortCode;
233         }
234         if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
235             out += ", nanoAppMessage = " + mNanoAppMessage;
236         }
237         if (mEventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
238             out += ", clientAuthState = " + mClientAuthorizationState;
239         }
240 
241         return out + "]";
242     }
243 
244     @Override
equals(@ullable Object object)245     public boolean equals(@Nullable Object object) {
246         if (object == this) {
247             return true;
248         }
249 
250         boolean isEqual = false;
251         if (object instanceof ContextHubIntentEvent) {
252             ContextHubIntentEvent other = (ContextHubIntentEvent) object;
253             if (other.getEventType() == mEventType
254                     && other.getContextHubInfo().equals(mContextHubInfo)) {
255                 isEqual = true;
256                 try {
257                     if (mEventType != ContextHubManager.EVENT_HUB_RESET) {
258                         isEqual &= (other.getNanoAppId() == mNanoAppId);
259                     }
260                     if (mEventType == ContextHubManager.EVENT_NANOAPP_ABORTED) {
261                         isEqual &= (other.getNanoAppAbortCode() == mNanoAppAbortCode);
262                     }
263                     if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
264                         isEqual &= other.getNanoAppMessage().equals(mNanoAppMessage);
265                     }
266                     if (mEventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) {
267                         isEqual &= other.getClientAuthorizationState() == mClientAuthorizationState;
268                     }
269                 } catch (UnsupportedOperationException e) {
270                     isEqual = false;
271                 }
272             }
273         }
274 
275         return isEqual;
276     }
277 
hasExtraOrThrow(Intent intent, String extra)278     private static void hasExtraOrThrow(Intent intent, String extra) {
279         if (!intent.hasExtra(extra)) {
280             throw new IllegalArgumentException("Intent did not have extra: " + extra);
281         }
282     }
283 
getIntExtraOrThrow(Intent intent, String extra)284     private static int getIntExtraOrThrow(Intent intent, String extra) {
285         hasExtraOrThrow(intent, extra);
286         return intent.getIntExtra(extra, -1 /* defaultValue */);
287     }
288 
getLongExtraOrThrow(Intent intent, String extra)289     private static long getLongExtraOrThrow(Intent intent, String extra) {
290         hasExtraOrThrow(intent, extra);
291         return intent.getLongExtra(extra, -1 /* defaultValue */);
292     }
293 }
294