content: /*
   * Copyright 2018 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package android.hardware.location;
  
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.annotation.SystemApi;
  import android.app.PendingIntent;
  import android.content.Intent;
  
  import com.android.internal.util.Preconditions;
  
  /**
   * A helper class to retrieve information about a Intent event received for a PendingIntent
   * registered with {@link ContextHubManager.createClient(ContextHubInfo, PendingIntent, long)}.
   * This object can only be created through the factory method
   * {@link ContextHubIntentEvent.fromIntent(Intent)}.
   *
   * @hide
   */
  @SystemApi
  public class ContextHubIntentEvent {
      @ContextHubManager.Event private final int mEventType;
  
      @NonNull private final ContextHubInfo mContextHubInfo;
  
      private final long mNanoAppId;
  
      private final NanoAppMessage mNanoAppMessage;
  
      private final int mNanoAppAbortCode;
  
      private ContextHubIntentEvent(
              @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
              long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode) {
          mContextHubInfo = contextHubInfo;
          mEventType = eventType;
          mNanoAppId = nanoAppId;
          mNanoAppMessage = nanoAppMessage;
          mNanoAppAbortCode = nanoAppAbortCode;
      }
  
      private ContextHubIntentEvent(
              @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType) {
          this(contextHubInfo, eventType, -1 /* nanoAppId */, null /* nanoAppMessage */,
                  -1 /* nanoAppAbortCode */);
      }
  
      private ContextHubIntentEvent(
              @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
              long nanoAppId) {
          this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */,
                  -1 /* nanoAppAbortCode */);
      }
  
      private ContextHubIntentEvent(
              @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
              long nanoAppId, @NonNull NanoAppMessage nanoAppMessage) {
          this(contextHubInfo, eventType, nanoAppId, nanoAppMessage, -1 /* nanoAppAbortCode */);
      }
  
      private ContextHubIntentEvent(
              @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType,
              long nanoAppId, int nanoAppAbortCode) {
          this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, nanoAppAbortCode);
      }
  
      /**
       * Creates a ContextHubIntentEvent object from an Intent received through a PendingIntent
       * registered with {@link ContextHubManager.createClient(ContextHubInfo, PendingIntent, long)}.
       *
       * @param intent the Intent object from an Intent event
       * @return the ContextHubIntentEvent object describing the event
       *
       * @throws IllegalArgumentException if the Intent was not a valid intent
       */
      @NonNull
      public static ContextHubIntentEvent fromIntent(@NonNull Intent intent) {
          Preconditions.checkNotNull(intent, "Intent cannot be null");
  
          hasExtraOrThrow(intent, ContextHubManager.EXTRA_CONTEXT_HUB_INFO);
          ContextHubInfo info = intent.getParcelableExtra(ContextHubManager.EXTRA_CONTEXT_HUB_INFO);
          if (info == null) {
              throw new IllegalArgumentException("ContextHubInfo extra was null");
          }
  
          int eventType = getIntExtraOrThrow(intent, ContextHubManager.EXTRA_EVENT_TYPE);
          ContextHubIntentEvent event;
          switch (eventType) {
              case ContextHubManager.EVENT_NANOAPP_LOADED:
              case ContextHubManager.EVENT_NANOAPP_UNLOADED:
              case ContextHubManager.EVENT_NANOAPP_ENABLED:
              case ContextHubManager.EVENT_NANOAPP_DISABLED:
              case ContextHubManager.EVENT_NANOAPP_ABORTED:
              case ContextHubManager.EVENT_NANOAPP_MESSAGE: // fall through
                  long nanoAppId = getLongExtraOrThrow(intent, ContextHubManager.EXTRA_NANOAPP_ID);
                  if (eventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
                      hasExtraOrThrow(intent, ContextHubManager.EXTRA_MESSAGE);
                      NanoAppMessage message =
                              intent.getParcelableExtra(ContextHubManager.EXTRA_MESSAGE);
                      if (message == null) {
                          throw new IllegalArgumentException("NanoAppMessage extra was null");
                      }
  
                      event = new ContextHubIntentEvent(info, eventType, nanoAppId, message);
                  } else if (eventType == ContextHubManager.EVENT_NANOAPP_ABORTED) {
                      int nanoAppAbortCode = getIntExtraOrThrow(
                              intent, ContextHubManager.EXTRA_NANOAPP_ABORT_CODE);
                      event = new ContextHubIntentEvent(info, eventType, nanoAppId, nanoAppAbortCode);
                  } else {
                      event = new ContextHubIntentEvent(info, eventType, nanoAppId);
                  }
                  break;
  
              case ContextHubManager.EVENT_HUB_RESET:
                  event = new ContextHubIntentEvent(info, eventType);
                  break;
  
              default:
                  throw new IllegalArgumentException("Unknown intent event type " + eventType);
          }
  
          return event;
      }
  
      /**
       * @return the event type of this Intent event
       */
      @ContextHubManager.Event
      public int getEventType() {
          return mEventType;
      }
  
      /**
       * @return the ContextHubInfo object describing the Context Hub this event was for
       */
      @NonNull
      public ContextHubInfo getContextHubInfo() {
          return mContextHubInfo;
      }
  
      /**
       * @return the ID of the nanoapp this event was for
       *
       * @throws UnsupportedOperationException if the event did not have a nanoapp associated
       */
      public long getNanoAppId() {
          if (mEventType == ContextHubManager.EVENT_HUB_RESET) {
              throw new UnsupportedOperationException(
                      "Cannot invoke getNanoAppId() on Context Hub reset event");
          }
          return mNanoAppId;
      }
  
      /**
       * @return the nanoapp's abort code
       *
       * @throws UnsupportedOperationException if this was not a nanoapp abort event
       */
      public int getNanoAppAbortCode() {
          if (mEventType != ContextHubManager.EVENT_NANOAPP_ABORTED) {
              throw new UnsupportedOperationException(
                      "Cannot invoke getNanoAppAbortCode() on non-abort event: " + mEventType);
          }
          return mNanoAppAbortCode;
      }
  
      /**
       * @return the message from a nanoapp
       *
       * @throws UnsupportedOperationException if this was not a nanoapp message event
       */
      @NonNull
      public NanoAppMessage getNanoAppMessage() {
          if (mEventType != ContextHubManager.EVENT_NANOAPP_MESSAGE) {
              throw new UnsupportedOperationException(
                      "Cannot invoke getNanoAppMessage() on non-message event: " + mEventType);
          }
          return mNanoAppMessage;
      }
  
      @Override
      public String toString() {
          String out = "ContextHubIntentEvent[eventType = " + mEventType
                  + ", contextHubId = " + mContextHubInfo.getId();
  
          if (mEventType != ContextHubManager.EVENT_HUB_RESET) {
              out += ", nanoAppId = 0x" + Long.toHexString(mNanoAppId);
          }
          if (mEventType == ContextHubManager.EVENT_NANOAPP_ABORTED) {
              out += ", nanoAppAbortCode = " + mNanoAppAbortCode;
          }
          if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
              out += ", nanoAppMessage = " + mNanoAppMessage;
          }
  
          return out + "]";
      }
  
      @Override
      public boolean equals(@Nullable Object object) {
          if (object == this) {
              return true;
          }
  
          boolean isEqual = false;
          if (object instanceof ContextHubIntentEvent) {
              ContextHubIntentEvent other = (ContextHubIntentEvent) object;
              if (other.getEventType() == mEventType
                      && other.getContextHubInfo().equals(mContextHubInfo)) {
                  isEqual = true;
                  try {
                      if (mEventType != ContextHubManager.EVENT_HUB_RESET) {
                          isEqual &= (other.getNanoAppId() == mNanoAppId);
                      }
                      if (mEventType == ContextHubManager.EVENT_NANOAPP_ABORTED) {
                          isEqual &= (other.getNanoAppAbortCode() == mNanoAppAbortCode);
                      }
                      if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
                          isEqual &= other.getNanoAppMessage().equals(mNanoAppMessage);
                      }
                  } catch (UnsupportedOperationException e) {
                      isEqual = false;
                  }
              }
          }
  
          return isEqual;
      }
  
      private static void hasExtraOrThrow(Intent intent, String extra) {
          if (!intent.hasExtra(extra)) {
              throw new IllegalArgumentException("Intent did not have extra: " + extra);
          }
      }
  
      private static int getIntExtraOrThrow(Intent intent, String extra) {
          hasExtraOrThrow(intent, extra);
          return intent.getIntExtra(extra, -1 /* defaultValue */);
      }
  
      private static long getLongExtraOrThrow(Intent intent, String extra) {
          hasExtraOrThrow(intent, extra);
          return intent.getLongExtra(extra, -1 /* defaultValue */);
      }
  }
  