Merge "camera: Add device info version"
diff --git a/broadcastradio/2.0/Android.bp b/broadcastradio/2.0/Android.bp
index 1d7861e..7409005 100644
--- a/broadcastradio/2.0/Android.bp
+++ b/broadcastradio/2.0/Android.bp
@@ -8,7 +8,9 @@
     },
     srcs: [
         "types.hal",
+        "IAnnouncementObserver.hal",
         "IBroadcastRadio.hal",
+        "ICloseHandle.hal",
         "ITunerCallback.hal",
         "ITunerSession.hal",
     ],
@@ -16,8 +18,14 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "AmFmBandRange",
+        "AmFmRegionConfig",
+        "Announcement",
+        "AnnouncementType",
         "ConfigFlag",
         "Constants",
+        "DabTableEntry",
+        "Deemphasis",
         "IdentifierType",
         "Metadata",
         "MetadataKey",
@@ -28,6 +36,7 @@
         "ProgramListChunk",
         "ProgramSelector",
         "Properties",
+        "Rds",
         "Result",
         "VendorKeyValue",
     ],
diff --git a/broadcastradio/2.0/IAnnouncementObserver.hal b/broadcastradio/2.0/IAnnouncementObserver.hal
new file mode 100644
index 0000000..c91e29a
--- /dev/null
+++ b/broadcastradio/2.0/IAnnouncementObserver.hal
@@ -0,0 +1,30 @@
+/* Copyright (C) 2017 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.broadcastradio@2.0;
+
+/**
+ * Callback interface for announcement observer.
+ *
+ * For typical configuration, the observer is a broadcast radio service.
+ */
+interface IAnnouncementObserver {
+    /**
+     * Called whenever announcement list has changed.
+     *
+     * @param announcements The complete list of currently active announcements.
+     */
+    oneway onListUpdated(vec<Announcement> announcements);
+};
diff --git a/broadcastradio/2.0/IBroadcastRadio.hal b/broadcastradio/2.0/IBroadcastRadio.hal
index 3ab1cc2..7578f44 100644
--- a/broadcastradio/2.0/IBroadcastRadio.hal
+++ b/broadcastradio/2.0/IBroadcastRadio.hal
@@ -15,6 +15,8 @@
 
 package android.hardware.broadcastradio@2.0;
 
+import IAnnouncementObserver;
+import ICloseHandle;
 import ITunerCallback;
 import ITunerSession;
 
@@ -33,6 +35,28 @@
     getProperties() generates (Properties properties);
 
     /**
+     * Fetches current or possible AM/FM region configuration.
+     *
+     * @param full If true, returns full hardware capabilities.
+     *             If false, returns current regional configuration.
+     * @return result OK in case of success.
+     *                NOT_SUPPORTED if the tuner doesn't support AM/FM.
+     * @return config Hardware capabilities (full=true) or
+     *                current configuration (full=false).
+     */
+    getAmFmRegionConfig(bool full)
+            generates (Result result, AmFmRegionConfig config);
+
+    /**
+     * Fetches current DAB region configuration.
+     *
+     * @return result OK in case of success.
+     *                NOT_SUPPORTED if the tuner doesn't support DAB.
+     * @return config Current configuration.
+     */
+    getDabRegionConfig() generates (Result result, vec<DabTableEntry> config);
+
+    /**
      * Opens a new tuner session.
      *
      * There may be only one session active at a time. If the new session was
@@ -44,7 +68,7 @@
      * @return session The session interface.
      */
     openSession(ITunerCallback callback)
-            generates (Result result, ITunerSession session);
+        generates (Result result, ITunerSession session);
 
     /**
      * Fetch image from radio module cache.
@@ -78,4 +102,28 @@
      *               or a zero-length vector if identifier doesn't exist.
      */
     getImage(uint32_t id) generates (vec<uint8_t> image);
+
+    /**
+     * Registers announcement observer.
+     *
+     * If there is at least one observer registered, HAL implementation must
+     * notify about announcements even if no sessions are active.
+     *
+     * If the observer dies, the HAL implementation must unregister observer
+     * automatically.
+     *
+     * @param enabled The list of announcement types to watch for.
+     * @param cb The callback interface.
+     * @return result OK in case of success.
+     *                NOT_SUPPORTED if the tuner doesn't support announcements.
+     * @return closeHandle A handle to unregister observer,
+     *                     nullptr if result was not OK.
+     */
+    registerAnnouncementObserver(
+            vec<AnnouncementType> enabled,
+            IAnnouncementObserver cb
+        ) generates (
+            Result result,
+            ICloseHandle closeHandle
+        );
 };
diff --git a/broadcastradio/2.0/ICloseHandle.hal b/broadcastradio/2.0/ICloseHandle.hal
new file mode 100644
index 0000000..34cea14
--- /dev/null
+++ b/broadcastradio/2.0/ICloseHandle.hal
@@ -0,0 +1,32 @@
+/* Copyright (C) 2017 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.broadcastradio@2.0;
+
+/**
+ * Represents a generic close handle to remove a callback that doesn't need
+ * active interface.
+ */
+interface ICloseHandle {
+    /**
+     * Closes the handle.
+     *
+     * The call must not fail and must only be issued once.
+     *
+     * After the close call is executed, no other calls to this interface
+     * are allowed.
+     */
+    close();
+};
diff --git a/broadcastradio/2.0/ITunerSession.hal b/broadcastradio/2.0/ITunerSession.hal
index a3f93fd..a58fa62 100644
--- a/broadcastradio/2.0/ITunerSession.hal
+++ b/broadcastradio/2.0/ITunerSession.hal
@@ -154,7 +154,7 @@
      * @return results Operation completion status for parameters being set.
      */
     setParameters(vec<VendorKeyValue> parameters)
-            generates (vec<VendorKeyValue> results);
+        generates (vec<VendorKeyValue> results);
 
     /**
      * Generic method for retrieving vendor-specific parameter values.
diff --git a/broadcastradio/2.0/default/Android.bp b/broadcastradio/2.0/default/Android.bp
index 6d4effb..900454e 100644
--- a/broadcastradio/2.0/default/Android.bp
+++ b/broadcastradio/2.0/default/Android.bp
@@ -24,6 +24,9 @@
         "-Wextra",
         "-Werror",
     ],
+    cppflags: [
+        "-std=c++1z",
+    ],
     srcs: [
         "BroadcastRadio.cpp",
         "TunerSession.cpp",
diff --git a/broadcastradio/2.0/default/BroadcastRadio.cpp b/broadcastradio/2.0/default/BroadcastRadio.cpp
index 5ab517d..d16aaff 100644
--- a/broadcastradio/2.0/default/BroadcastRadio.cpp
+++ b/broadcastradio/2.0/default/BroadcastRadio.cpp
@@ -33,6 +33,16 @@
 using std::mutex;
 using std::vector;
 
+static const AmFmRegionConfig gDefaultAmFmConfig = {  //
+    {
+        {87500, 108000, 100, 100},  // FM
+        {153, 282, 3, 9},           // AM LW
+        {531, 1620, 9, 9},          // AM MW
+        {1600, 30000, 1, 5},        // AM SW
+    },
+    static_cast<uint32_t>(Deemphasis::D50),
+    static_cast<uint32_t>(Rds::RDS)};
+
 static Properties initProperties(const VirtualRadio& virtualRadio) {
     Properties prop = {};
 
@@ -51,7 +61,9 @@
 }
 
 BroadcastRadio::BroadcastRadio(const VirtualRadio& virtualRadio)
-    : mVirtualRadio(virtualRadio), mProperties(initProperties(virtualRadio)) {}
+    : mVirtualRadio(virtualRadio),
+      mProperties(initProperties(virtualRadio)),
+      mAmFmConfig(gDefaultAmFmConfig) {}
 
 Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb) {
     ALOGV("%s", __func__);
@@ -59,6 +71,44 @@
     return {};
 }
 
+AmFmRegionConfig BroadcastRadio::getAmFmConfig() const {
+    lock_guard<mutex> lk(mMut);
+    return mAmFmConfig;
+}
+
+Return<void> BroadcastRadio::getAmFmRegionConfig(bool full, getAmFmRegionConfig_cb _hidl_cb) {
+    ALOGV("%s(%d)", __func__, full);
+
+    if (full) {
+        AmFmRegionConfig config = {};
+        config.ranges = hidl_vec<AmFmBandRange>({
+            {65000, 108000, 10, 0},  // FM
+            {150, 30000, 1, 0},      // AM
+        });
+        config.fmDeemphasis = Deemphasis::D50 | Deemphasis::D75;
+        config.fmRds = Rds::RDS | Rds::RBDS;
+        _hidl_cb(Result::OK, config);
+        return {};
+    } else {
+        _hidl_cb(Result::OK, getAmFmConfig());
+        return {};
+    }
+}
+
+Return<void> BroadcastRadio::getDabRegionConfig(getDabRegionConfig_cb _hidl_cb) {
+    ALOGV("%s", __func__);
+
+    hidl_vec<DabTableEntry> config = {
+        {"5A", 174928},  {"7D", 194064},  {"8A", 195936},  {"8B", 197648},  {"9A", 202928},
+        {"9B", 204640},  {"9C", 206352},  {"10B", 211648}, {"10C", 213360}, {"10D", 215072},
+        {"11A", 216928}, {"11B", 218640}, {"11C", 220352}, {"11D", 222064}, {"12A", 223936},
+        {"12B", 225648}, {"12C", 227360}, {"12D", 229072},
+    };
+
+    _hidl_cb(Result::OK, config);
+    return {};
+}
+
 Return<void> BroadcastRadio::openSession(const sp<ITunerCallback>& callback,
                                          openSession_cb _hidl_cb) {
     ALOGV("%s", __func__);
@@ -91,6 +141,15 @@
     return {};
 }
 
+Return<void> BroadcastRadio::registerAnnouncementObserver(
+    const hidl_vec<AnnouncementType>& enabled, const sp<IAnnouncementObserver>& /* cb */,
+    registerAnnouncementObserver_cb _hidl_cb) {
+    ALOGV("%s(%s)", __func__, toString(enabled).c_str());
+
+    _hidl_cb(Result::NOT_SUPPORTED, nullptr);
+    return {};
+}
+
 }  // namespace implementation
 }  // namespace V2_0
 }  // namespace broadcastradio
diff --git a/broadcastradio/2.0/default/BroadcastRadio.h b/broadcastradio/2.0/default/BroadcastRadio.h
index fcf0615..7904946 100644
--- a/broadcastradio/2.0/default/BroadcastRadio.h
+++ b/broadcastradio/2.0/default/BroadcastRadio.h
@@ -32,14 +32,22 @@
 
     // V2_0::IBroadcastRadio methods
     Return<void> getProperties(getProperties_cb _hidl_cb) override;
+    Return<void> getAmFmRegionConfig(bool full, getAmFmRegionConfig_cb _hidl_cb);
+    Return<void> getDabRegionConfig(getDabRegionConfig_cb _hidl_cb);
     Return<void> openSession(const sp<ITunerCallback>& callback, openSession_cb _hidl_cb) override;
     Return<void> getImage(uint32_t id, getImage_cb _hidl_cb);
+    Return<void> registerAnnouncementObserver(const hidl_vec<AnnouncementType>& enabled,
+                                              const sp<IAnnouncementObserver>& cb,
+                                              registerAnnouncementObserver_cb _hidl_cb);
 
     std::reference_wrapper<const VirtualRadio> mVirtualRadio;
     Properties mProperties;
 
+    AmFmRegionConfig getAmFmConfig() const;
+
    private:
-    std::mutex mMut;
+    mutable std::mutex mMut;
+    AmFmRegionConfig mAmFmConfig;
     wp<TunerSession> mSession;
 };
 
diff --git a/broadcastradio/2.0/default/TunerSession.cpp b/broadcastradio/2.0/default/TunerSession.cpp
index 244544a..3166d86 100644
--- a/broadcastradio/2.0/default/TunerSession.cpp
+++ b/broadcastradio/2.0/default/TunerSession.cpp
@@ -77,8 +77,12 @@
     mCallback->onCurrentProgramInfoChanged(programInfo);
 }
 
+const BroadcastRadio& TunerSession::module() const {
+    return mModule.get();
+}
+
 const VirtualRadio& TunerSession::virtualRadio() const {
-    return mModule.get().mVirtualRadio;
+    return module().mVirtualRadio;
 }
 
 Return<Result> TunerSession::tune(const ProgramSelector& sel) {
@@ -86,7 +90,7 @@
     lock_guard<mutex> lk(mMut);
     if (mIsClosed) return Result::INVALID_STATE;
 
-    if (!utils::isSupported(mModule.get().mProperties, sel)) {
+    if (!utils::isSupported(module().mProperties, sel)) {
         ALOGW("Selector not supported");
         return Result::NOT_SUPPORTED;
     }
@@ -170,23 +174,19 @@
     mIsTuneCompleted = false;
 
     auto stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY);
-#if 0
-    // TODO(b/69958423): handle regions
-    if (directionUp) {
-        stepTo += mAmfmConfig.spacings[0];
-    } else {
-        stepTo -= mAmfmConfig.spacings[0];
+    auto range = getAmFmRangeLocked();
+    if (!range) {
+        ALOGE("Can't find current band");
+        return Result::INTERNAL_ERROR;
     }
 
-    if (stepTo > mAmfmConfig.upperLimit) stepTo = mAmfmConfig.lowerLimit;
-    if (stepTo < mAmfmConfig.lowerLimit) stepTo = mAmfmConfig.upperLimit;
-#else
     if (directionUp) {
-        stepTo += 100;
+        stepTo += range->spacing;
     } else {
-        stepTo -= 100;
+        stepTo -= range->spacing;
     }
-#endif
+    if (stepTo > range->upperBound) stepTo = range->lowerBound;
+    if (stepTo < range->lowerBound) stepTo = range->upperBound;
 
     auto task = [this, stepTo]() {
         ALOGI("Performing step to %s", std::to_string(stepTo).c_str());
@@ -280,6 +280,18 @@
     return {};
 }
 
+std::optional<AmFmBandRange> TunerSession::getAmFmRangeLocked() const {
+    if (!mIsTuneCompleted) return {};
+    if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) return {};
+
+    auto freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY);
+    for (auto&& range : module().getAmFmConfig().ranges) {
+        if (range.lowerBound <= freq && range.upperBound >= freq) return range;
+    }
+
+    return {};
+}
+
 }  // namespace implementation
 }  // namespace V2_0
 }  // namespace broadcastradio
diff --git a/broadcastradio/2.0/default/TunerSession.h b/broadcastradio/2.0/default/TunerSession.h
index a58aa19..5d27b1e 100644
--- a/broadcastradio/2.0/default/TunerSession.h
+++ b/broadcastradio/2.0/default/TunerSession.h
@@ -22,6 +22,8 @@
 #include <android/hardware/broadcastradio/2.0/ITunerSession.h>
 #include <broadcastradio-utils/WorkerThread.h>
 
+#include <optional>
+
 namespace android {
 namespace hardware {
 namespace broadcastradio {
@@ -48,6 +50,8 @@
                                        getParameters_cb _hidl_cb) override;
     virtual Return<void> close() override;
 
+    std::optional<AmFmBandRange> getAmFmRangeLocked() const;
+
    private:
     std::mutex mMut;
     WorkerThread mThread;
@@ -61,6 +65,7 @@
 
     void tuneInternalLocked(const ProgramSelector& sel);
     const VirtualRadio& virtualRadio() const;
+    const BroadcastRadio& module() const;
 };
 
 }  // namespace implementation
diff --git a/broadcastradio/2.0/types.hal b/broadcastradio/2.0/types.hal
index b5264f4..c5880e2 100644
--- a/broadcastradio/2.0/types.hal
+++ b/broadcastradio/2.0/types.hal
@@ -32,6 +32,7 @@
 enum Result : int32_t {
     OK,
     UNKNOWN_ERROR,
+    INTERNAL_ERROR,
     INVALID_ARGUMENTS,
     INVALID_STATE,
     NOT_SUPPORTED,
@@ -120,6 +121,108 @@
 };
 
 /**
+ * A supported or configured RDS variant.
+ *
+ * Both might be set for hardware capabilities check (with full=true when
+ * calling getAmFmRegionConfig), but only one (or none) for specific
+ * region settings.
+ */
+enum Rds : uint8_t {
+    /** Standard variant, used everywhere except North America. */
+    RDS = 1 << 0,
+
+    /** Variant used in North America. */
+    RBDS = 1 << 1,
+};
+
+/**
+ * FM de-emphasis filter supported or configured.
+ *
+ * Both might be set for hardware capabilities check (with full=true when
+ * calling getAmFmRegionConfig), but exactly one for specific region settings.
+ */
+enum Deemphasis : uint8_t {
+    D50 = 1 << 0,
+    D75 = 1 << 1,
+};
+
+/**
+ * Regional configuration for AM/FM.
+ *
+ * For hardware capabilities check (with full=true when calling
+ * getAmFmRegionConfig), HAL implementation fills entire supported range of
+ * frequencies and features.
+ *
+ * When checking current configuration, at most one bit in each bitfield
+ * can be set.
+ */
+struct AmFmRegionConfig {
+    /**
+     * All supported or configured AM/FM bands.
+     *
+     * AM/FM bands are identified by frequency value
+     * (see IdentifierType::AMFM_FREQUENCY).
+     *
+     * With typical configuration, it's expected to have two frequency ranges
+     * for capabilities check (AM and FM) and four ranges for specific region
+     * configuration (AM LW, AM MW, AM SW, FM).
+     */
+    vec<AmFmBandRange> ranges;
+
+    /** De-emphasis filter supported/configured. */
+    bitfield<Deemphasis> fmDeemphasis;
+
+    /** RDS/RBDS variant supported/configured. */
+    bitfield<Rds> fmRds;
+};
+
+/**
+ * AM/FM band range for region configuration.
+ *
+ * Defines channel grid: each possible channel is set at
+ * lowerBound + channelNumber * spacing, up to upperBound.
+ */
+struct AmFmBandRange {
+    /** The frequency of the first channel within the range. */
+    uint32_t lowerBound;
+
+    /** The frequency of the last channel within the range. */
+    uint32_t upperBound;
+
+    /** Channel grid resolution, how far apart are the channels. */
+    uint32_t spacing;
+
+    /**
+     * Spacing used when scanning for channels. It's a multiply of spacing and
+     * allows to skip some channels when scanning to make it faster.
+     *
+     * Tuner may first quickly check every n-th channel and if it detects echo
+     * from a station, it fine-tunes to find the exact frequency.
+     *
+     * It's ignored for capabilities check (with full=true when calling
+     * getAmFmRegionConfig).
+     */
+    uint32_t scanSpacing;
+};
+
+/**
+ * An entry in regional configuration for DAB.
+ *
+ * Defines a frequency table row for ensembles.
+ */
+struct DabTableEntry {
+    /**
+     * Channel name, i.e. 5A, 7B.
+     *
+     * It must match the following regular expression: /^[A-Z0-9]{2,5}$/.
+     */
+    string label;
+
+    /** Frequency, in kHz. */
+    uint32_t frequency;
+};
+
+/**
  * Properties of a given broadcast radio module.
  */
 struct Properties {
@@ -363,7 +466,12 @@
      * Consists of (from the LSB):
      * - 32bit: Station ID number;
      * - 4bit: HD Radio subchannel;
-     * - 18bit: AMFM_FREQUENCY. // TODO(b/69958777): is it necessary?
+     * - 18bit: AMFM_FREQUENCY.
+     *
+     * While station ID number should be unique globally, it sometimes get
+     * abused by broadcasters (i.e. not being set at all). To ensure local
+     * uniqueness, AMFM_FREQUENCY was added here. Global uniqueness is
+     * a best-effort - see HD_STATION_NAME.
      *
      * HD Radio subchannel is a value in range 0-7.
      * This index is 0-based (where 0 is MPS and 1..7 are SPS),
@@ -375,6 +483,22 @@
     HD_STATION_ID_EXT,
 
     /**
+     * 64bit additional identifier for HD Radio.
+     *
+     * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
+     * globally unique. To provide a best-effort solution, a short version of
+     * station name may be carried as additional identifier and may be used
+     * by the tuner hardware to double-check tuning.
+     *
+     * The name is limited to the first 8 A-Z0-9 characters (lowercase letters
+     * must be converted to uppercase). Encoded in little-endian ASCII:
+     * the first character of the name is the LSB.
+     *
+     * For example: "Abc" is encoded as 0x434241.
+     */
+    HD_STATION_NAME,
+
+    /**
      * 28bit compound primary identifier for Digital Audio Broadcasting.
      *
      * Consists of (from the LSB):
@@ -389,7 +513,7 @@
      * The remaining bits should be set to zeros when writing on the chip side
      * and ignored when read.
      */
-    DAB_SID_EXT = HD_STATION_ID_EXT + 2,
+    DAB_SID_EXT,
 
     /** 16bit */
     DAB_ENSEMBLE,
@@ -674,3 +798,55 @@
      */
     bool excludeModifications;
 };
+
+/**
+ * Type of an announcement.
+ *
+ * It maps to different announcement types per each radio technology.
+ */
+enum AnnouncementType : uint8_t {
+    /** DAB alarm, RDS emergency program type (PTY 31). */
+    EMERGENCY = 1,
+
+    /** DAB warning. */
+    WARNING,
+
+    /** DAB road traffic, RDS TA, HD Radio transportation. */
+    TRAFFIC,
+
+    /** Weather. */
+    WEATHER,
+
+    /** News. */
+    NEWS,
+
+    /** DAB event, special event. */
+    EVENT,
+
+    /** DAB sport report, RDS sports. */
+    SPORT,
+
+    /** All others. */
+    MISC,
+};
+
+/**
+ * A pointer to a station broadcasting active announcement.
+ */
+struct Announcement {
+    /**
+     * Program selector to tune to the announcement.
+     */
+    ProgramSelector selector;
+
+    /** Announcement type. */
+    AnnouncementType type;
+
+    /**
+     * Vendor-specific information.
+     *
+     * It may be used for extra features, not supported by the platform,
+     * for example: com.me.hdradio.urgency=100; com.me.hdradio.certainity=50.
+     */
+    vec<VendorKeyValue> vendorInfo;
+};
diff --git a/broadcastradio/2.0/vts/functional/Android.bp b/broadcastradio/2.0/vts/functional/Android.bp
index 6017b15..6940bca 100644
--- a/broadcastradio/2.0/vts/functional/Android.bp
+++ b/broadcastradio/2.0/vts/functional/Android.bp
@@ -17,6 +17,9 @@
 cc_test {
     name: "VtsHalBroadcastradioV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
+    cppflags: [
+        "-std=c++1z",
+    ],
     srcs: ["VtsHalBroadcastradioV2_0TargetTest.cpp"],
     static_libs: [
         "android.hardware.broadcastradio@2.0",
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index cbe6288..1111478 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -26,9 +26,12 @@
 #include <broadcastradio-vts-utils/call-barrier.h>
 #include <broadcastradio-vts-utils/mock-timeout.h>
 #include <broadcastradio-vts-utils/pointer-utils.h>
+#include <cutils/bitops.h>
 #include <gmock/gmock.h>
 
 #include <chrono>
+#include <optional>
+#include <regex>
 
 namespace android {
 namespace hardware {
@@ -87,12 +90,18 @@
     utils::ProgramInfoSet mProgramList;
 };
 
+struct AnnouncementObserverMock : public IAnnouncementObserver {
+    MOCK_METHOD1(onListUpdated, Return<void>(const hidl_vec<Announcement>&));
+};
+
 class BroadcastRadioHalTest : public ::testing::VtsHalHidlTargetTestBase {
    protected:
     virtual void SetUp() override;
     virtual void TearDown() override;
 
     bool openSession();
+    bool getAmFmRegionConfig(bool full, AmFmRegionConfig* config);
+    std::optional<utils::ProgramInfoSet> getProgramList();
 
     sp<IBroadcastRadio> mModule;
     Properties mProperties;
@@ -159,6 +168,41 @@
     return nullptr != mSession.get();
 }
 
+bool BroadcastRadioHalTest::getAmFmRegionConfig(bool full, AmFmRegionConfig* config) {
+    auto halResult = Result::UNKNOWN_ERROR;
+    auto cb = [&](Result result, AmFmRegionConfig configCb) {
+        halResult = result;
+        if (config) *config = configCb;
+    };
+
+    auto hidlResult = mModule->getAmFmRegionConfig(full, cb);
+    EXPECT_TRUE(hidlResult.isOk());
+
+    if (halResult == Result::NOT_SUPPORTED) return false;
+
+    EXPECT_EQ(Result::OK, halResult);
+    return halResult == Result::OK;
+}
+
+std::optional<utils::ProgramInfoSet> BroadcastRadioHalTest::getProgramList() {
+    EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
+
+    auto startResult = mSession->startProgramListUpdates({});
+    if (startResult == Result::NOT_SUPPORTED) {
+        printSkipped("Program list not supported");
+        return nullopt;
+    }
+    EXPECT_EQ(Result::OK, startResult);
+    if (startResult != Result::OK) return nullopt;
+
+    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
+
+    auto stopResult = mSession->stopProgramListUpdates();
+    EXPECT_TRUE(stopResult.isOk());
+
+    return mCallback->mProgramList;
+}
+
 /**
  * Test session opening.
  *
@@ -181,6 +225,127 @@
     ASSERT_TRUE(openSession());
 }
 
+static bool isValidAmFmFreq(uint64_t freq) {
+    auto id = utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq);
+    return utils::isValid(id);
+}
+
+static void validateRange(const AmFmBandRange& range) {
+    EXPECT_TRUE(isValidAmFmFreq(range.lowerBound));
+    EXPECT_TRUE(isValidAmFmFreq(range.upperBound));
+    EXPECT_LT(range.lowerBound, range.upperBound);
+    EXPECT_GT(range.spacing, 0u);
+    EXPECT_EQ(0u, (range.upperBound - range.lowerBound) % range.spacing);
+}
+
+static bool supportsFM(const AmFmRegionConfig& config) {
+    for (auto&& range : config.ranges) {
+        if (utils::getBand(range.lowerBound) == utils::FrequencyBand::FM) return true;
+    }
+    return false;
+}
+
+/**
+ * Test fetching AM/FM regional configuration.
+ *
+ * Verifies that:
+ *  - AM/FM regional configuration is either set at startup or not supported at all by the hardware;
+ *  - there is at least one AM/FM band configured;
+ *  - FM Deemphasis and RDS are correctly configured for FM-capable radio;
+ *  - all channel grids (frequency ranges and spacings) are valid;
+ *  - scan spacing is a multiply of manual spacing value.
+ */
+TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfig) {
+    AmFmRegionConfig config;
+    bool supported = getAmFmRegionConfig(false, &config);
+    if (!supported) {
+        printSkipped("AM/FM not supported");
+        return;
+    }
+
+    EXPECT_GT(config.ranges.size(), 0u);
+    EXPECT_LE(popcountll(config.fmDeemphasis), 1);
+    EXPECT_LE(popcountll(config.fmRds), 1);
+
+    for (auto&& range : config.ranges) {
+        validateRange(range);
+        EXPECT_EQ(0u, range.scanSpacing % range.spacing);
+        EXPECT_GE(range.scanSpacing, range.spacing);
+    }
+
+    if (supportsFM(config)) {
+        EXPECT_EQ(popcountll(config.fmDeemphasis), 1);
+    }
+}
+
+/**
+ * Test fetching AM/FM regional capabilities.
+ *
+ * Verifies that:
+ *  - AM/FM regional capabilities are either available or not supported at all by the hardware;
+ *  - there is at least one AM/FM range supported;
+ *  - there is at least one de-emphasis filter mode supported for FM-capable radio;
+ *  - all channel grids (frequency ranges and spacings) are valid;
+ *  - scan spacing is not set.
+ */
+TEST_F(BroadcastRadioHalTest, GetAmFmRegionConfigCapabilities) {
+    AmFmRegionConfig config;
+    bool supported = getAmFmRegionConfig(true, &config);
+    if (!supported) {
+        printSkipped("AM/FM not supported");
+        return;
+    }
+
+    EXPECT_GT(config.ranges.size(), 0u);
+
+    for (auto&& range : config.ranges) {
+        validateRange(range);
+        EXPECT_EQ(0u, range.scanSpacing);
+    }
+
+    if (supportsFM(config)) {
+        EXPECT_GE(popcountll(config.fmDeemphasis), 1);
+    }
+}
+
+/**
+ * Test fetching DAB regional configuration.
+ *
+ * Verifies that:
+ *  - DAB regional configuration is either set at startup or not supported at all by the hardware;
+ *  - all channel labels match correct format;
+ *  - all channel frequencies are in correct range.
+ */
+TEST_F(BroadcastRadioHalTest, GetDabRegionConfig) {
+    Result halResult;
+    hidl_vec<DabTableEntry> config;
+    auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
+        halResult = result;
+        config = configCb;
+    };
+    auto hidlResult = mModule->getDabRegionConfig(cb);
+    ASSERT_TRUE(hidlResult.isOk());
+
+    if (halResult == Result::NOT_SUPPORTED) {
+        printSkipped("DAB not supported");
+        return;
+    }
+    ASSERT_EQ(Result::OK, halResult);
+
+    std::regex re("^[A-Z0-9]{2,5}$");
+    // double-check correctness of the test
+    ASSERT_TRUE(std::regex_match("5A", re));
+    ASSERT_FALSE(std::regex_match("5a", re));
+    ASSERT_FALSE(std::regex_match("123ABC", re));
+
+    for (auto&& entry : config) {
+        EXPECT_TRUE(std::regex_match(std::string(entry.label), re));
+
+        auto id = utils::make_identifier(IdentifierType::DAB_FREQUENCY, entry.frequency);
+        EXPECT_TRUE(utils::isValid(id));
+    }
+}
+
 /**
  * Test tuning with FM selector.
  *
@@ -505,19 +670,69 @@
 TEST_F(BroadcastRadioHalTest, GetProgramList) {
     ASSERT_TRUE(openSession());
 
-    EXPECT_TIMEOUT_CALL(*mCallback, onProgramListReady).Times(AnyNumber());
+    getProgramList();
+}
 
-    auto startResult = mSession->startProgramListUpdates({});
-    if (startResult == Result::NOT_SUPPORTED) {
-        printSkipped("Program list not supported");
+/**
+ * Test HD_STATION_NAME correctness.
+ *
+ * Verifies that if a program on the list contains HD_STATION_NAME identifier:
+ *  - the program provides station name in its metadata;
+ *  - the identifier matches the name;
+ *  - there is only one identifier of that type.
+ */
+TEST_F(BroadcastRadioHalTest, HdRadioStationNameId) {
+    ASSERT_TRUE(openSession());
+
+    auto list = getProgramList();
+    if (!list) return;
+
+    for (auto&& program : *list) {
+        auto nameIds = utils::getAllIds(program.selector, IdentifierType::HD_STATION_NAME);
+        EXPECT_LE(nameIds.size(), 1u);
+        if (nameIds.size() == 0) continue;
+
+        auto name = utils::getMetadataString(program, MetadataKey::PROGRAM_NAME);
+        if (!name) name = utils::getMetadataString(program, MetadataKey::RDS_PS);
+        ASSERT_TRUE(name.has_value());
+
+        auto expectedId = utils::make_hdradio_station_name(*name);
+        EXPECT_EQ(expectedId.value, nameIds[0]);
+    }
+}
+
+/**
+ * Test announcement observer registration.
+ *
+ * Verifies that:
+ *  - registerAnnouncementObserver either succeeds or returns NOT_SUPPORTED;
+ *  - if it succeeds, it returns a valid close handle (which is a nullptr otherwise);
+ *  - closing handle does not crash.
+ */
+TEST_F(BroadcastRadioHalTest, AnnouncementObserverRegistration) {
+    sp<AnnouncementObserverMock> observer = new AnnouncementObserverMock();
+
+    Result halResult = Result::UNKNOWN_ERROR;
+    sp<ICloseHandle> closeHandle = nullptr;
+    auto cb = [&](Result result, const sp<ICloseHandle>& closeHandle_) {
+        halResult = result;
+        closeHandle = closeHandle_;
+    };
+
+    auto hidlResult =
+        mModule->registerAnnouncementObserver({AnnouncementType::EMERGENCY}, observer, cb);
+    ASSERT_TRUE(hidlResult.isOk());
+
+    if (halResult == Result::NOT_SUPPORTED) {
+        ASSERT_EQ(nullptr, closeHandle.get());
+        printSkipped("Announcements not supported");
         return;
     }
-    ASSERT_EQ(Result::OK, startResult);
 
-    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onProgramListReady, timeout::programListScan);
+    ASSERT_EQ(Result::OK, halResult);
+    ASSERT_NE(nullptr, closeHandle.get());
 
-    auto stopResult = mSession->stopProgramListUpdates();
-    EXPECT_TRUE(stopResult.isOk());
+    closeHandle->close();
 }
 
 // TODO(b/70939328): test ProgramInfo's currentlyTunedId and
diff --git a/broadcastradio/common/tests/Android.bp b/broadcastradio/common/tests/Android.bp
index 512c02e..f6a3b6f 100644
--- a/broadcastradio/common/tests/Android.bp
+++ b/broadcastradio/common/tests/Android.bp
@@ -22,6 +22,9 @@
         "-Wextra",
         "-Werror",
     ],
+    cppflags: [
+        "-std=c++1z",
+    ],
     srcs: [
         "CommonXX_test.cpp",
     ],
@@ -43,8 +46,12 @@
         "-Wextra",
         "-Werror",
     ],
+    cppflags: [
+        "-std=c++1z",
+    ],
     srcs: [
         "IdentifierIterator_test.cpp",
+        "ProgramIdentifier_test.cpp",
     ],
     static_libs: [
         "android.hardware.broadcastradio@common-utils-2x-lib",
diff --git a/broadcastradio/common/tests/ProgramIdentifier_test.cpp b/broadcastradio/common/tests/ProgramIdentifier_test.cpp
new file mode 100644
index 0000000..51ad014
--- /dev/null
+++ b/broadcastradio/common/tests/ProgramIdentifier_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <broadcastradio-utils-2x/Utils.h>
+#include <gtest/gtest.h>
+
+#include <optional>
+
+namespace {
+
+namespace utils = android::hardware::broadcastradio::utils;
+
+TEST(ProgramIdentifierTest, hdRadioStationName) {
+    auto verify = [](std::string name, uint64_t nameId) {
+        auto id = utils::make_hdradio_station_name(name);
+        EXPECT_EQ(nameId, id.value) << "Failed to convert '" << name << "'";
+    };
+
+    verify("", 0);
+    verify("Abc", 0x434241);
+    verify("Some Station 1", 0x54415453454d4f53);
+    verify("Station1", 0x314e4f4954415453);
+    verify("!@#$%^&*()_+", 0);
+    verify("-=[]{};':\"0", 0x30);
+}
+
+}  // anonymous namespace
diff --git a/broadcastradio/common/utils2x/Android.bp b/broadcastradio/common/utils2x/Android.bp
index c6b94af..aab94f2 100644
--- a/broadcastradio/common/utils2x/Android.bp
+++ b/broadcastradio/common/utils2x/Android.bp
@@ -23,6 +23,9 @@
         "-Wextra",
         "-Werror",
     ],
+    cppflags: [
+        "-std=c++1z",
+    ],
     srcs: [
         "Utils.cpp",
     ],
diff --git a/broadcastradio/common/utils2x/Utils.cpp b/broadcastradio/common/utils2x/Utils.cpp
index e0337b4..6fe9554 100644
--- a/broadcastradio/common/utils2x/Utils.cpp
+++ b/broadcastradio/common/utils2x/Utils.cpp
@@ -89,6 +89,18 @@
     return IdentifierIterator(sel) + 1 /* primary id */ + sel.secondaryIds.size();
 }
 
+FrequencyBand getBand(uint64_t freq) {
+    // keep in sync with
+    // frameworks/base/services/core/java/com/android/server/broadcastradio/hal2/Utils.java
+    if (freq < 30) return FrequencyBand::UNKNOWN;
+    if (freq < 500) return FrequencyBand::AM_LW;
+    if (freq < 1705) return FrequencyBand::AM_MW;
+    if (freq < 30000) return FrequencyBand::AM_SW;
+    if (freq < 60000) return FrequencyBand::UNKNOWN;
+    if (freq < 110000) return FrequencyBand::FM;
+    return FrequencyBand::UNKNOWN;
+}
+
 static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
                        const IdentifierType type) {
     return hasId(a, type) && hasId(b, type);
@@ -194,7 +206,7 @@
     return false;
 }
 
-static bool isValid(const ProgramIdentifier& id) {
+bool isValid(const ProgramIdentifier& id) {
     auto val = id.value;
     bool valid = true;
 
@@ -209,8 +221,10 @@
         case IdentifierType::INVALID:
             expect(false, "IdentifierType::INVALID");
             break;
-        case IdentifierType::AMFM_FREQUENCY:
         case IdentifierType::DAB_FREQUENCY:
+            expect(val > 100000u, "f > 100MHz");
+        // fallthrough
+        case IdentifierType::AMFM_FREQUENCY:
         case IdentifierType::DRMO_FREQUENCY:
             expect(val > 100u, "f > 100kHz");
             expect(val < 10000000u, "f < 10GHz");
@@ -231,6 +245,15 @@
             expect(freq < 10000000u, "f < 10GHz");
             break;
         }
+        case IdentifierType::HD_STATION_NAME: {
+            while (val > 0) {
+                auto ch = static_cast<char>(val & 0xFF);
+                val >>= 8;
+                expect((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z'),
+                       "HD_STATION_NAME does not match [A-Z0-9]+");
+            }
+            break;
+        }
         case IdentifierType::DAB_SID_EXT: {
             auto sid = val & 0xFFFF;  // 16bit
             val >>= 16;
@@ -353,6 +376,40 @@
     }
 }
 
+std::optional<std::string> getMetadataString(const V2_0::ProgramInfo& info,
+                                             const V2_0::MetadataKey key) {
+    auto isKey = [key](const V2_0::Metadata& item) {
+        return static_cast<V2_0::MetadataKey>(item.key) == key;
+    };
+
+    auto it = std::find_if(info.metadata.begin(), info.metadata.end(), isKey);
+    if (it == info.metadata.end()) return std::nullopt;
+
+    return it->stringValue;
+}
+
+V2_0::ProgramIdentifier make_hdradio_station_name(const std::string& name) {
+    constexpr size_t maxlen = 8;
+
+    std::string shortName;
+    shortName.reserve(maxlen);
+
+    auto&& loc = std::locale::classic();
+    for (char ch : name) {
+        if (!std::isalnum(ch, loc)) continue;
+        shortName.push_back(std::toupper(ch, loc));
+        if (shortName.length() >= maxlen) break;
+    }
+
+    uint64_t val = 0;
+    for (auto rit = shortName.rbegin(); rit != shortName.rend(); ++rit) {
+        val <<= 8;
+        val |= static_cast<uint8_t>(*rit);
+    }
+
+    return make_identifier(IdentifierType::HD_STATION_NAME, val);
+}
+
 }  // namespace utils
 }  // namespace broadcastradio
 }  // namespace hardware
diff --git a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
index bac11fd..5e51941 100644
--- a/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
+++ b/broadcastradio/common/utils2x/include/broadcastradio-utils-2x/Utils.h
@@ -18,6 +18,7 @@
 
 #include <android/hardware/broadcastradio/2.0/types.h>
 #include <chrono>
+#include <optional>
 #include <queue>
 #include <thread>
 #include <unordered_set>
@@ -27,6 +28,14 @@
 namespace broadcastradio {
 namespace utils {
 
+enum class FrequencyBand {
+    UNKNOWN,
+    FM,
+    AM_LW,
+    AM_MW,
+    AM_SW,
+};
+
 V2_0::IdentifierType getType(uint32_t typeAsInt);
 V2_0::IdentifierType getType(const V2_0::ProgramIdentifier& id);
 
@@ -64,6 +73,16 @@
 IdentifierIterator end(const V2_0::ProgramSelector& sel);
 
 /**
+ * Guesses band from the frequency value.
+ *
+ * The band bounds are not exact to cover multiple regions.
+ * The function is biased towards success, i.e. it never returns
+ * FrequencyBand::UNKNOWN for correct frequency, but a result for
+ * incorrect one is undefined (it doesn't have to return UNKNOWN).
+ */
+FrequencyBand getBand(uint64_t frequency);
+
+/**
  * Checks, if {@code pointer} tunes to {@channel}.
  *
  * For example, having a channel {AMFM_FREQUENCY = 103.3}:
@@ -105,6 +124,7 @@
  */
 bool isSupported(const V2_0::Properties& prop, const V2_0::ProgramSelector& sel);
 
+bool isValid(const V2_0::ProgramIdentifier& id);
 bool isValid(const V2_0::ProgramSelector& sel);
 
 V2_0::ProgramIdentifier make_identifier(V2_0::IdentifierType type, uint64_t value);
@@ -127,6 +147,11 @@
 
 void updateProgramList(ProgramInfoSet& list, const V2_0::ProgramListChunk& chunk);
 
+std::optional<std::string> getMetadataString(const V2_0::ProgramInfo& info,
+                                             const V2_0::MetadataKey key);
+
+V2_0::ProgramIdentifier make_hdradio_station_name(const std::string& name);
+
 }  // namespace utils
 }  // namespace broadcastradio
 }  // namespace hardware
diff --git a/gnss/1.0/default/Gnss.cpp b/gnss/1.0/default/Gnss.cpp
index f362749..32c131c 100644
--- a/gnss/1.0/default/Gnss.cpp
+++ b/gnss/1.0/default/Gnss.cpp
@@ -121,7 +121,7 @@
     svStatus.numSvs = status->num_svs;
 
     if (svStatus.numSvs > static_cast<uint32_t>(GnssMax::SVS_COUNT)) {
-        ALOGW("Too many satellites %zd. Clamps to %d.", svStatus.numSvs, GnssMax::SVS_COUNT);
+        ALOGW("Too many satellites %u. Clamps to %d.", svStatus.numSvs, GnssMax::SVS_COUNT);
         svStatus.numSvs = static_cast<uint32_t>(GnssMax::SVS_COUNT);
     }
 
@@ -191,7 +191,7 @@
      * GnssMax::SVS_COUNT entries.
      */
     if (svStatus.numSvs > static_cast<uint32_t>(GnssMax::SVS_COUNT)) {
-        ALOGW("Too many satellites %zd. Clamps to %d.", svStatus.numSvs, GnssMax::SVS_COUNT);
+        ALOGW("Too many satellites %u. Clamps to %d.", svStatus.numSvs, GnssMax::SVS_COUNT);
         svStatus.numSvs = static_cast<uint32_t>(GnssMax::SVS_COUNT);
     }
 
diff --git a/radio/1.2/Android.bp b/radio/1.2/Android.bp
index cdb9bf6..3e678bb 100644
--- a/radio/1.2/Android.bp
+++ b/radio/1.2/Android.bp
@@ -19,6 +19,7 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "CardStatus",
         "CellIdentityCdma",
         "CellIdentityGsm",
         "CellIdentityLte",
@@ -35,6 +36,8 @@
         "NetworkScanResult",
         "RadioConst",
         "ScanIntervalRange",
+        "SimSlotStatus",
+        "SlotState",
     ],
     gen_java: true,
 }
diff --git a/radio/1.2/IRadio.hal b/radio/1.2/IRadio.hal
index 6ae78a0..73f1024 100644
--- a/radio/1.2/IRadio.hal
+++ b/radio/1.2/IRadio.hal
@@ -37,4 +37,46 @@
      * Response function is IRadioResponse.startNetworkScanResponse()
      */
     oneway startNetworkScan_1_2(int32_t serial, NetworkScanRequest request);
+
+    /**
+     * Get SIM Slot status.
+     *
+     * Request provides the slot status of all active and inactive SIM slots and whether card is
+     * present in the slots or not.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getSimSlotsStatusResponse()
+     */
+    oneway getSimSlotsStatus(int32_t serial);
+
+    /**
+     * Set SIM Slot mapping.
+
+     * Maps the logical slots to the physical slots. Logical slot is the slot that is seen by modem.
+     * Physical slot is the actual physical slot. Request maps the physical slot to logical slot.
+     * Logical slots that are already mapped to the requested physical slot are not impacted.
+     *
+     * Example no. of logical slots 1 and physical slots 2:
+     * The only logical slot (index 0) can be mapped to first physical slot (value 0) or second
+     * physical slot(value 1), while the other physical slot remains unmapped and inactive.
+     * slotMap[0] = 1 or slotMap[0] = 0
+     *
+     * Example no. of logical slots 2 and physical slots 2:
+     * First logical slot (index 0) can be mapped to physical slot 1 or 2 and other logical slot
+     * can be mapped to other physical slot. Each logical slot must be mapped to a physical slot.
+     * slotMap[0] = 0 and slotMap[1] = 1 or slotMap[0] = 1 and slotMap[1] = 0
+     *
+     * @param serial Serial number of request
+     * @param slotMap Logical to physical slot mapping, size == no. of radio instances. Index is
+     *        mapping to logical slot and value to physical slot, need to provide all the slots
+     *        mapping when sending request in case of multi slot device.
+     *        EX: uint32_t slotMap[logical slot] = physical slot
+     *        index 0 is the first logical_slot number of logical slots is equal to number of Radio
+     *        instances and number of physical slots is equal to size of slotStatus in
+     *        getSimSlotsStatusResponse
+     *
+     * Response callback is IRadioResponse.setSimSlotsMappingResponse()
+     */
+    oneway setSimSlotsMapping(int32_t serial, vec<uint32_t> slotMap);
 };
diff --git a/radio/1.2/IRadioIndication.hal b/radio/1.2/IRadioIndication.hal
index 5d3efcf..4aae74d 100644
--- a/radio/1.2/IRadioIndication.hal
+++ b/radio/1.2/IRadioIndication.hal
@@ -28,4 +28,13 @@
      * Incremental network scan results
      */
     oneway networkScanResult_1_2(RadioIndicationType type, NetworkScanResult result);
-};
\ No newline at end of file
+
+    /**
+     * Indicates SIM slot status change.
+     *
+     * @param type Type of radio indication
+     * @param slotStatus new slot status info with size equals to the number of physical slots on
+     *        the device
+     */
+    oneway simSlotsStatusChanged(RadioIndicationType type, vec<SimSlotStatus> slotStatus);
+};
diff --git a/radio/1.2/IRadioResponse.hal b/radio/1.2/IRadioResponse.hal
index 5bfbbae..cf6bc00 100644
--- a/radio/1.2/IRadioResponse.hal
+++ b/radio/1.2/IRadioResponse.hal
@@ -41,4 +41,42 @@
      *   RadioError:CANCELLED
      */
     oneway getCellInfoListResponse_1_2(RadioResponseInfo info, vec<CellInfo> cellInfo);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param cardStatus ICC card status as defined by CardStatus in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     */
+    oneway getIccCardStatusResponse_1_2(RadioResponseInfo info, CardStatus cardStatus);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param slotStatus Sim slot struct containing all the physical SIM slots info with size
+      *       equals to the number of physical slots on the device
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:NO_MEMORY
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:MODEM_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     */
+    oneway getSimSlotsStatusResponse(RadioResponseInfo info, vec<SimSlotStatus> slotStatus);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:NO_MEMORY
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:MODEM_ERR
+     */
+    oneway setSimSlotsMappingResponse(RadioResponseInfo info);
 };
diff --git a/radio/1.2/types.hal b/radio/1.2/types.hal
index 52af575..1e28d3b 100644
--- a/radio/1.2/types.hal
+++ b/radio/1.2/types.hal
@@ -17,6 +17,8 @@
 package android.hardware.radio@1.2;
 
 import @1.0::CdmaSignalStrength;
+import @1.0::CardState;
+import @1.0::CardStatus;
 import @1.0::CellIdentityCdma;
 import @1.0::CellIdentityGsm;
 import @1.0::CellIdentityLte;
@@ -187,37 +189,97 @@
      * Cell type for selecting from union CellInfo.
      */
     CellInfoType cellInfoType;
-    /*
+    /**
      * True if this cell is registered false if not registered.
      */
     bool registered;
-    /*
+    /**
      * Type of time stamp represented by timeStamp.
      */
     TimeStampType timeStampType;
-    /*
+    /**
      * Time in nanos as returned by ril_nano_time.
      */
     uint64_t timeStamp;
-    /*
+    /**
      * Only one of the below vectors must be of size 1 based on the CellInfoType and others must be
      * of size 0.
      */
     vec<CellInfoGsm> gsm;
-    /*
+    /**
      * Valid only if type = cdma and size = 1 else must be empty.
      */
     vec<CellInfoCdma> cdma;
-    /*
+    /**
      * Valid only if type = lte and size = 1 else must be empty.
      */
     vec<CellInfoLte> lte;
-    /*
+    /**
      * Valid only if type = wcdma and size = 1 else must be empty.
      */
     vec<CellInfoWcdma> wcdma;
-    /*
+    /**
      * Valid only if type = tdscdma and size = 1 else must be empty.
      */
     vec<CellInfoTdscdma> tdscdma;
 };
+
+enum SlotState : int32_t {
+    /**
+     * Physical slot is inactive
+     */
+    INACTIVE  = 0x00,
+    /**
+     * Physical slot is active
+     */
+    ACTIVE    = 0x01,
+};
+
+struct CardStatus {
+    @1.0::CardStatus base;
+    uint32_t physicalSlotId;
+    /**
+     * An Answer To Reset (ATR) is a message output by a Smart Card conforming to ISO/IEC 7816
+     * standards, following electrical reset of the card's chip. The ATR conveys information about
+     * the communication parameters proposed by the card, and the card's nature and state.
+     *
+     * This data is applicable only when cardState is CardState:PRESENT.
+     */
+    string atr;
+    /**
+     * Integrated Circuit Card IDentifier (ICCID) is Unique Identifier of the SIM CARD. File is
+     * located in the SIM card at EFiccid (0x2FE2) as per ETSI 102.221. The ICCID is defined by
+     * the ITU-T recommendation E.118 ISO/IEC 7816.
+     *
+     * This data is applicable only when cardState is CardState:PRESENT.
+     */
+    string iccid;
+};
+
+struct SimSlotStatus {
+    /**
+     * Card state in the physical slot
+     */
+    CardState cardState;
+    /**
+     * Slot state Active/Inactive
+     */
+    SlotState slotState;
+    /**
+     * An Answer To Reset (ATR) is a message output by a Smart Card conforming to ISO/IEC 7816
+     * standards, following electrical reset of the card's chip. The ATR conveys information about
+     * the communication parameters proposed by the card, and the card's nature and state.
+     *
+     * This data is applicable only when cardState is CardState:PRESENT.
+     */
+    string atr;
+    uint32_t logicalSlotId;
+    /**
+     * Integrated Circuit Card IDentifier (ICCID) is Unique Identifier of the SIM CARD. File is
+     * located in the SIM card at EFiccid (0x2FE2) as per ETSI 102.221. The ICCID is defined by
+     * the ITU-T recommendation E.118 ISO/IEC 7816.
+     *
+     * This data is applicable only when cardState is CardState:PRESENT.
+     */
+    string iccid;
+};
diff --git a/tests/memory/1.0/IMemoryTest.hal b/tests/memory/1.0/IMemoryTest.hal
index b20859c..3c4d4f3 100644
--- a/tests/memory/1.0/IMemoryTest.hal
+++ b/tests/memory/1.0/IMemoryTest.hal
@@ -23,6 +23,6 @@
     haveSomeMemory(memory mem) generates(memory mem);
     fillMemory(memory memory_in, uint8_t filler);
     haveSomeMemoryBlock(MemoryBlock blk) generates(MemoryBlock blk);
-    set(memory mem) generates();
+    set(memory mem);
     get()generates(IMemoryToken token);
 };