Merge 39b73943e9eadadcb898efb96ab373215229e506 on remote branch

Change-Id: I613515ea2de765462a9089d5ba5df7c285a7fa3c
diff --git a/audio/include/system/audio-base-utils.h b/audio/include/system/audio-base-utils.h
index 525a383..c3d3d53 100644
--- a/audio/include/system/audio-base-utils.h
+++ b/audio/include/system/audio-base-utils.h
@@ -134,6 +134,7 @@
     AUDIO_USAGE_MAX           = AUDIO_USAGE_CALL_ASSISTANT,
     AUDIO_USAGE_CNT           = AUDIO_USAGE_CALL_ASSISTANT + 1,
 
+    AUDIO_LATENCY_MODE_INVALID = -1,
     AUDIO_LATENCY_MODE_CNT    = AUDIO_LATENCY_MODE_LOW + 1,
 }; // enum
 
diff --git a/audio/include/system/audio.h b/audio/include/system/audio.h
index 82b75e0..6b4343f 100644
--- a/audio/include/system/audio.h
+++ b/audio/include/system/audio.h
@@ -344,6 +344,41 @@
             && (channelMask & AUDIO_CHANNEL_OUT_QUAD) == AUDIO_CHANNEL_OUT_QUAD;
 }
 
+/*
+ * MediaFormat channel masks follow the Java channel mask spec
+ * but might be specified as a native channel mask.  This method
+ * does a "smart" correction to ensure a native channel mask.
+ */
+static inline audio_channel_mask_t
+audio_channel_mask_from_media_format_mask(int32_t channelMaskFromFormat) {
+    // KEY_CHANNEL_MASK follows the android.media.AudioFormat java mask
+    // which is left-bitshifted by 2 relative to the native mask
+    if ((channelMaskFromFormat & 0b11) != 0) {
+        // received an unexpected mask (supposed to follow AudioFormat constants
+        // for output masks with the 2 least-significant bits at 0), but
+        // it may come from an extractor that uses native masks: keeping
+        // the mask as given is ok as it contains at least mono or stereo
+        // and potentially the haptic channels
+        return (audio_channel_mask_t)channelMaskFromFormat;
+    } else {
+        // We exclude bits from the lowest haptic bit all the way to the top of int.
+        // to avoid aliasing.  The remainder bits are position bits
+        // which must be shifted by 2 from Java to get native.
+        //
+        // Using the lowest set bit exclusion AND mask (x - 1), we find
+        // all the bits from lowest set bit to the top is m = x | ~(x - 1).
+        // Using the one's complement to two's complement formula ~x = -x - 1,
+        // we can reduce this to m = x | -x.
+        // (Note -x is also the lowest bit extraction AND mask; i.e. lowest_bit = x & -x).
+        const int32_t EXCLUDE_BITS = AUDIO_CHANNEL_HAPTIC_ALL | -AUDIO_CHANNEL_HAPTIC_ALL;
+        const int32_t positionBits = (channelMaskFromFormat & ~EXCLUDE_BITS) >> 2;
+
+        // Haptic bits are identical between Java and native.
+        const int32_t hapticBits = channelMaskFromFormat & AUDIO_CHANNEL_HAPTIC_ALL;
+        return (audio_channel_mask_t)(positionBits | hapticBits);
+    }
+}
+
 /**
  * Expresses the convention when stereo audio samples are stored interleaved
  * in an array.  This should improve readability by allowing code to use
diff --git a/camera/docs/ACameraMetadata.mako b/camera/docs/ACameraMetadata.mako
index effa5ea..d62fcc3 100644
--- a/camera/docs/ACameraMetadata.mako
+++ b/camera/docs/ACameraMetadata.mako
@@ -67,7 +67,7 @@
 // System tags that should be hidden from users
 std::unordered_set<uint32_t> ACameraMetadata::sSystemTags ({
     % for sec in find_all_sections(metadata):
-      % for entry in remove_synthetic_or_fwk_only(find_unique_entries(sec)):
+      % for entry in remove_hal_non_visible(find_unique_entries(sec)):
         % if entry.applied_visibility == "system":
     ${entry.name | csym},
         % endif
diff --git a/camera/docs/CameraDeviceInfo.mako b/camera/docs/CameraDeviceInfo.mako
index 1f89865..569a521 100644
--- a/camera/docs/CameraDeviceInfo.mako
+++ b/camera/docs/CameraDeviceInfo.mako
@@ -24,7 +24,9 @@
         HashSet<String> charsKeyNames = new HashSet<String>();
 % for sec in find_all_sections(metadata):
   % for entry in find_unique_entries(sec):
-    % if entry.kind == 'static' and entry.visibility in ("public", "java_public"):
+## TODO: Add fwk_java_public
+    % if entry.kind == 'static' and entry.visibility in \
+            ("public", "java_public"):
         charsKeyNames.add(CameraCharacteristics.${jkey_identifier(entry.name)}.getName());
     % endif
   % endfor
diff --git a/camera/docs/CameraMetadataEnums.mako b/camera/docs/CameraMetadataEnums.mako
index 0541804..995a83a 100644
--- a/camera/docs/CameraMetadataEnums.mako
+++ b/camera/docs/CameraMetadataEnums.mako
@@ -38,7 +38,8 @@
 ${value.sdk_notes | javadoc(metadata)}\
     % endif
      * @see ${target_class}#${entry.name | jkey_identifier}
-    % if entry.applied_visibility in ('hidden', 'ndk_public') or value.hidden:
+## TODO: Remove fwk_java_public
+    % if entry.applied_visibility in ('hidden', 'ndk_public', 'fwk_java_public') or value.hidden:
      * @hide
     %endif
     % if value.deprecated:
@@ -61,11 +62,13 @@
 % for outer_namespace in metadata.outer_namespaces: ## assumes single 'android' namespace
   % for section in outer_namespace.sections:
     % if section.find_first(lambda x: isinstance(x, metadata_model.Entry) and x.kind == xml_name) and \
-         any_visible(section, xml_name, ('public','hidden', 'ndk_public', 'java_public') ):
+         any_visible(section, xml_name, ('public','hidden', 'ndk_public', 'java_public', \
+         'fwk_java_public') ):
       % for inner_namespace in get_children_by_filtering_kind(section, xml_name, 'namespaces'):
 ## We only support 1 level of inner namespace, i.e. android.a.b and android.a.b.c works, but not android.a.b.c.d
 ## If we need to support more, we should use a recursive function here instead.. but the indentation gets trickier.
-        % for entry in filter_visibility(inner_namespace.entries, ('hidden','public', 'ndk_public', 'java_public')):
+        % for entry in filter_visibility(inner_namespace.entries, ('hidden','public', 'ndk_public', \
+        'java_public', 'fwk_pubic')):
           % if entry.enum \
               and not (entry.typedef and entry.typedef.languages.get('java')) \
               and not entry.is_clone():
@@ -75,7 +78,7 @@
       % endfor
       % for entry in filter_visibility( \
           get_children_by_filtering_kind(section, xml_name, 'entries'), \
-                                         ('hidden', 'public', 'ndk_public', 'java_public')):
+              ('hidden', 'public', 'ndk_public', 'java_public', 'fwk_java_public')):
         % if entry.enum \
              and not (entry.typedef and entry.typedef.languages.get('java')) \
              and not entry.is_clone():
diff --git a/camera/docs/CameraMetadataKeys.mako b/camera/docs/CameraMetadataKeys.mako
index ac3a920..e0e0054 100644
--- a/camera/docs/CameraMetadataKeys.mako
+++ b/camera/docs/CameraMetadataKeys.mako
@@ -54,14 +54,15 @@
      * @deprecated
 ${entry.deprecation_description | javadoc(metadata)}
   % endif
-  % if entry.applied_visibility in ('hidden', 'ndk_public', 'fwk_only'):
+  ## TODO: Remove fwk_java_public
+  % if entry.applied_visibility in ('hidden', 'ndk_public', 'fwk_only', 'fwk_java_public'):
      * @hide
   % endif
      */
   % if entry.deprecated:
     @Deprecated
   % endif
-  % if entry.applied_visibility in ('public', 'java_public'):
+  % if entry.applied_visibility in ('public', 'java_public', 'fwk_java_public'):
     @PublicKey
     @NonNull
   % endif
@@ -77,17 +78,17 @@
 % for outer_namespace in metadata.outer_namespaces: ## assumes single 'android' namespace
   % for section in outer_namespace.sections:
     % if section.find_first(lambda x: isinstance(x, metadata_model.Entry) and x.kind == xml_name) and \
-         any_visible(section, xml_name, ('public','hidden','ndk_public','java_public','fwk_only') ):
+         any_visible(section, xml_name, ('public','hidden','ndk_public','java_public','fwk_only','fwk_java_public') ):
       % for inner_namespace in get_children_by_filtering_kind(section, xml_name, 'namespaces'):
 ## We only support 1 level of inner namespace, i.e. android.a.b and android.a.b.c works, but not android.a.b.c.d
 ## If we need to support more, we should use a recursive function here instead.. but the indentation gets trickier.
-        % for entry in filter_visibility(inner_namespace.merged_entries, ('hidden','public', 'ndk_public', 'java_public ', 'fwk_only')):
+        % for entry in filter_visibility(inner_namespace.merged_entries, ('hidden','public','ndk_public','java_public','fwk_only','fwk_java_public')):
 ${generate_key(entry)}
        % endfor
     % endfor
     % for entry in filter_visibility( \
         get_children_by_filtering_kind(section, xml_name, 'merged_entries'), \
-                                         ('hidden', 'public', 'ndk_public', 'java_public', 'fwk_only')):
+               ('hidden', 'public', 'ndk_public', 'java_public', 'fwk_only', 'fwk_java_public')):
 ${generate_key(entry)}
     % endfor
     % endif
diff --git a/camera/docs/HidlMetadata.mako b/camera/docs/HidlMetadata.mako
index f9323d9..e3e4e88 100644
--- a/camera/docs/HidlMetadata.mako
+++ b/camera/docs/HidlMetadata.mako
@@ -105,7 +105,7 @@
 enum CameraMetadataTag : ${'uint32_t' if first_hal_minor_version(hal_major_version()) == hal_minor_version() else '@%d.%d::CameraMetadataTag' % (hal_major_version(), hal_minor_version()-1)} {
     % for sec in find_all_sections(metadata):
 <%    gotEntries = False %>\
-      % for idx,entry in enumerate(filter_added_in_hal_version(remove_synthetic_or_fwk_only(find_unique_entries(sec)), hal_major_version(), hal_minor_version())):
+      % for idx,entry in enumerate(filter_added_in_hal_version(remove_hal_non_visible(find_unique_entries(sec)), hal_major_version(), hal_minor_version())):
 <%      gotEntries = True %>\
     /** ${entry.name} [${entry.kind}, ${annotated_type(entry)}, ${entry.applied_visibility}]
         % if entry.description:
@@ -136,7 +136,7 @@
  * Enumeration definitions for the various entries that need them
  */
 % for sec in find_all_sections(metadata):
-  % for entry in filter_has_enum_values_added_in_hal_version(remove_synthetic_or_fwk_only(find_unique_entries(sec)), hal_major_version(), hal_minor_version()):
+  % for entry in filter_has_enum_values_added_in_hal_version(remove_hal_non_visible(find_unique_entries(sec)), hal_major_version(), hal_minor_version()):
     % if entry.enum:
 
 <%    isFirstValue = True %>\
diff --git a/camera/docs/aidl/CameraMetadataEnum.mako b/camera/docs/aidl/CameraMetadataEnum.mako
index 02822b3..7c6b364 100644
--- a/camera/docs/aidl/CameraMetadataEnum.mako
+++ b/camera/docs/aidl/CameraMetadataEnum.mako
@@ -51,7 +51,7 @@
   _entry = None
   _enum_name = None
   for sec in find_all_sections(metadata):
-    for entry in remove_synthetic_or_fwk_only(find_unique_entries(sec)):
+    for entry in remove_hal_non_visible(find_unique_entries(sec)):
       if entry.name == enum():
         _entry = entry
         _enum_name = entry.name.removeprefix("android.")
diff --git a/camera/docs/aidl/CameraMetadataTag.mako b/camera/docs/aidl/CameraMetadataTag.mako
index c4a3be5..e79fd64 100644
--- a/camera/docs/aidl/CameraMetadataTag.mako
+++ b/camera/docs/aidl/CameraMetadataTag.mako
@@ -57,7 +57,7 @@
 <% gap = False %>\
 % for sec_idx,sec in enumerate(find_all_sections(metadata)):
   % for idx,entry in enumerate(remove_synthetic(find_unique_entries(sec))):
-    % if entry.visibility == 'fwk_only':
+    % if entry.visibility in ('fwk_only', 'fwk_java_public'):
 <% gap = True %>\
 <% curIdx += 1 %>\
 <% continue %>\
diff --git a/camera/docs/camera_device_info.mako b/camera/docs/camera_device_info.mako
index f213f54..846d2a4 100644
--- a/camera/docs/camera_device_info.mako
+++ b/camera/docs/camera_device_info.mako
@@ -100,7 +100,7 @@
   idx = section_idx * pow(2,16)
 %>\
 % for entry in find_unique_entries(sec):
-% if entry.kind == 'static' and entry.visibility in ("public", "java_public"):
+% if entry.kind == 'static' and entry.visibility in ("public", "java_public", "fwk_java_public"):
     ${protobuf_type(entry)} ${protobuf_name(entry)} = ${idx};
 <%
     idx += 1
diff --git a/camera/docs/camera_device_info.proto b/camera/docs/camera_device_info.proto
index 24ca312..908f0de 100644
--- a/camera/docs/camera_device_info.proto
+++ b/camera/docs/camera_device_info.proto
@@ -165,6 +165,7 @@
     optional int32 android_sensor_orientation = 983050;
     repeated int32 android_sensor_availableTestPatternModes = 983051;
     repeated Rect android_sensor_opticalBlackRegions = 983052;
+    optional int32 android_sensor_readoutTimestamp = 983053;
     optional Rect android_sensor_info_activeArraySize = 1048576;
     optional RangeInt android_sensor_info_sensitivityRange = 1048577;
     optional int32 android_sensor_info_colorFilterArrangement = 1048578;
diff --git a/camera/docs/camera_metadata_asserts.mako b/camera/docs/camera_metadata_asserts.mako
index 94da986..f92c790 100644
--- a/camera/docs/camera_metadata_asserts.mako
+++ b/camera/docs/camera_metadata_asserts.mako
@@ -50,7 +50,7 @@
 #include <aidl/android/hardware/camera/metadata/CameraMetadataSectionStart.h>
 #include <aidl/android/hardware/camera/metadata/CameraMetadataTag.h>
 % for sec in find_all_sections(metadata):
-  % for entry in remove_synthetic_or_fwk_only(find_unique_entries(sec)):
+  % for entry in remove_hal_non_visible(find_unique_entries(sec)):
     % if entry.enum:
 #include <aidl/android/hardware/camera/metadata/${aidl_enum_name(entry)}.h>
     % endif
@@ -74,13 +74,13 @@
         == static_cast<int>(${aidl_camera_metadata_section_start("VENDOR_SECTION_START")}));
 
 % for sec in find_all_sections(metadata):
-  % for idx,entry in enumerate(remove_synthetic_or_fwk_only(find_unique_entries(sec))):
+  % for idx,entry in enumerate(remove_hal_non_visible(find_unique_entries(sec))):
 static_assert(static_cast<int>(${csym(entry.name)})
         == static_cast<int>(${aidl_camera_metadata_tag(csym(entry.name))}));
   % endfor
 % endfor
 % for sec in find_all_sections(metadata):
-  % for entry in remove_synthetic_or_fwk_only(find_unique_entries(sec)):
+  % for entry in remove_hal_non_visible(find_unique_entries(sec)):
     % if entry.enum:
 
       % for val in aidl_enum_values(entry):
diff --git a/camera/docs/docs.html b/camera/docs/docs.html
index 40f7a6e..10d4957 100644
--- a/camera/docs/docs.html
+++ b/camera/docs/docs.html
@@ -924,6 +924,8 @@
             ><a href="#static_android.sensor.opaqueRawSize">android.sensor.opaqueRawSize</a></li>
             <li
             ><a href="#static_android.sensor.opaqueRawSizeMaximumResolution">android.sensor.opaqueRawSizeMaximumResolution</a></li>
+            <li
+            ><a href="#static_android.sensor.readoutTimestamp">android.sensor.readoutTimestamp</a></li>
           </ul>
         </li>
         <li>
@@ -12085,6 +12087,10 @@
 <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#turnOnTorchWithStrengthLevel">CameraManager#turnOnTorchWithStrengthLevel</a>.<wbr/>
 If this value is equal to 1,<wbr/> flashlight brightness control is not supported.<wbr/>
 The value for this key will be null for devices with no flash unit.<wbr/></p>
+<p>The maximum value is guaranteed to be safe to use for an indefinite duration in
+terms of device flashlight lifespan,<wbr/> but may be too bright for comfort for many
+use cases.<wbr/> Use the default torch brightness value to avoid problems with an
+over-bright flashlight.<wbr/></p>
             </td>
           </tr>
 
@@ -27917,6 +27923,93 @@
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
         
+                
+          <tr class="entry" id="static_android.sensor.readoutTimestamp">
+            <td class="entry_name
+             " rowspan="5">
+              android.<wbr/>sensor.<wbr/>readout<wbr/>Timestamp
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name entry_type_name_enum">byte</span>
+
+              <span class="entry_type_visibility"> [fwk_java_public]</span>
+
+
+              <span class="entry_type_hwlevel">[legacy] </span>
+
+
+
+                <ul class="entry_type_enum">
+                  <li>
+                    <span class="entry_type_enum_name">NOT_SUPPORTED (v3.2)</span>
+                    <span class="entry_type_enum_notes"><p>This camera device doesn't support readout timestamp and onReadoutStarted
+callback.<wbr/></p></span>
+                  </li>
+                  <li>
+                    <span class="entry_type_enum_name">HARDWARE (v3.2)</span>
+                    <span class="entry_type_enum_notes"><p>This camera device supports the onReadoutStarted callback as well as outputting
+readout timestamp for streams with TIMESTAMP_<wbr/>BASE_<wbr/>READOUT_<wbr/>SENSOR timestamp base.<wbr/> The
+readout timestamp is generated by the camera hardware and it has the same accuracy
+and timing characteristics of the start-of-exposure time.<wbr/></p></span>
+                  </li>
+                </ul>
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>Whether or not the camera device supports readout timestamp and
+onReadoutStarted callback.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>2</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>If this tag is HARDWARE,<wbr/> the camera device calls onReadoutStarted in addition to the
+onCaptureStarted callback for each capture.<wbr/> The timestamp passed into the callback
+is the start of camera image readout rather than the start of the exposure.<wbr/> In
+addition,<wbr/> the application can configure an
+<a href="https://developer.android.com/reference/android/hardware/camera2/params/OutputConfiguration.html">OutputConfiguration</a> with
+TIMESTAMP_<wbr/>BASE_<wbr/>READOUT_<wbr/>SENSOR timestamp base,<wbr/> in which case,<wbr/> the timestamp of the
+output surface matches the timestamp from the corresponding onReadoutStarted callback.<wbr/></p>
+<p>The readout timestamp is beneficial for video recording,<wbr/> because the encoder favors
+uniform timestamps,<wbr/> and the readout timestamps better reflect the cadence camera sensors
+output data.<wbr/></p>
+<p>If this tag is HARDWARE,<wbr/> the camera device produces the start-of-exposure and
+start-of-readout together.<wbr/> As a result,<wbr/> the onReadoutStarted is called right after
+onCaptureStarted.<wbr/> The difference in start-of-readout and start-of-exposure is the sensor
+exposure time,<wbr/> plus certain constant offset.<wbr/> The offset is usually due to camera sensor
+level crop,<wbr/> and it remains constant for a given camera sensor mode.<wbr/></p>
+            </td>
+          </tr>
+
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">HAL Implementation Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>This property is populated by the camera framework and must not be set at the HAL layer.<wbr/></p>
+            </td>
+          </tr>
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
         
 
       <!-- end of kind -->
diff --git a/camera/docs/metadata_definitions.xml b/camera/docs/metadata_definitions.xml
index f83b7c0..b1eadc3 100644
--- a/camera/docs/metadata_definitions.xml
+++ b/camera/docs/metadata_definitions.xml
@@ -3769,6 +3769,11 @@
               {@link android.hardware.camera2.CameraManager#turnOnTorchWithStrengthLevel}.
               If this value is equal to 1, flashlight brightness control is not supported.
               The value for this key will be null for devices with no flash unit.
+
+              The maximum value is guaranteed to be safe to use for an indefinite duration in
+              terms of device flashlight lifespan, but may be too bright for comfort for many
+              use cases. Use the default torch brightness value to avoid problems with an
+              over-bright flashlight.
             </details>
           </entry>
           <entry name="strengthDefaultLevel" type="int32" visibility="public" hal_version="3.8">
@@ -10570,6 +10575,49 @@
           </details>
         </entry>
       </dynamic>
+      <static>
+        <entry name="readoutTimestamp" type="byte" visibility="fwk_java_public"
+          enum="true" hwlevel="legacy">
+        <enum>
+          <value>NOT_SUPPORTED
+            <notes>This camera device doesn't support readout timestamp and onReadoutStarted
+              callback.
+            </notes>
+          </value>
+          <value>HARDWARE
+            <notes>This camera device supports the onReadoutStarted callback as well as outputting
+              readout timestamp for streams with TIMESTAMP_BASE_READOUT_SENSOR timestamp base. The
+              readout timestamp is generated by the camera hardware and it has the same accuracy
+              and timing characteristics of the start-of-exposure time.
+            </notes>
+          </value>
+        </enum>
+        <description>Whether or not the camera device supports readout timestamp and
+          onReadoutStarted callback.</description>
+        <details>
+          If this tag is HARDWARE, the camera device calls onReadoutStarted in addition to the
+          onCaptureStarted callback for each capture. The timestamp passed into the callback
+          is the start of camera image readout rather than the start of the exposure. In
+          addition, the application can configure an
+          {@link android.hardware.camera2.params.OutputConfiguration} with
+          TIMESTAMP_BASE_READOUT_SENSOR timestamp base, in which case, the timestamp of the
+          output surface matches the timestamp from the corresponding onReadoutStarted callback.
+
+          The readout timestamp is beneficial for video recording, because the encoder favors
+          uniform timestamps, and the readout timestamps better reflect the cadence camera sensors
+          output data.
+
+          If this tag is HARDWARE, the camera device produces the start-of-exposure and
+          start-of-readout together. As a result, the onReadoutStarted is called right after
+          onCaptureStarted. The difference in start-of-readout and start-of-exposure is the sensor
+          exposure time, plus certain constant offset. The offset is usually due to camera sensor
+          level crop, and it remains constant for a given camera sensor mode.
+        </details>
+        <hal_details>
+          This property is populated by the camera framework and must not be set at the HAL layer.
+        </hal_details>
+      </entry>
+    </static>
     </section>
     <section name="shading">
       <controls>
diff --git a/camera/docs/metadata_definitions.xsd b/camera/docs/metadata_definitions.xsd
index 6df5813..2a16726 100644
--- a/camera/docs/metadata_definitions.xsd
+++ b/camera/docs/metadata_definitions.xsd
@@ -206,6 +206,7 @@
                     <enumeration value="hidden" /> <!-- java as @hide. Not included in NDK -->
                     <enumeration value="public" /> <!-- public to both java and NDK -->
                     <enumeration value="fwk_only" /> <!-- java as @hide. Not included in NDK. Not included in hal interfaces. -->
+                    <enumeration value="fwk_java_public" /> <!-- public to java. Not included in NDK. Not included in hal interfaces. -->
                 </restriction>
             </simpleType>
         </attribute>
diff --git a/camera/docs/metadata_enums.py b/camera/docs/metadata_enums.py
index 2949213..62b6330 100644
--- a/camera/docs/metadata_enums.py
+++ b/camera/docs/metadata_enums.py
@@ -39,7 +39,7 @@
   metadata = parser.metadata
 
   for sec in find_all_sections(metadata):
-    for entry in remove_synthetic_or_fwk_only(find_unique_entries(sec)):
+    for entry in remove_hal_non_visible(find_unique_entries(sec)):
       if entry.enum:
         enum_name = entry.name.removeprefix("android.")
         s = enum_name.split(".")
diff --git a/camera/docs/metadata_helpers.py b/camera/docs/metadata_helpers.py
index bd586df..76f7a61 100644
--- a/camera/docs/metadata_helpers.py
+++ b/camera/docs/metadata_helpers.py
@@ -852,7 +852,7 @@
     # Convert metadata entry "android.x.y.z" to form
     # "{@link CaptureRequest#X_Y_Z android.x.y.z}"
     def javadoc_crossref_filter(node):
-      if node.applied_visibility in ('public', 'java_public'):
+      if node.applied_visibility in ('public', 'java_public', 'fwk_java_public'):
         return '{@link %s#%s %s}' % (kind_mapping[node.kind],
                                      jkey_identifier(node.name),
                                      node.name)
@@ -862,7 +862,8 @@
     # For each public tag "android.x.y.z" referenced, add a
     # "@see CaptureRequest#X_Y_Z"
     def javadoc_crossref_see_filter(node_set):
-      node_set = (x for x in node_set if x.applied_visibility in ('public', 'java_public'))
+      node_set = (x for x in node_set if x.applied_visibility in \
+                  ('public', 'java_public', 'fwk_java_public'))
 
       text = '\n'
       for node in node_set:
@@ -1357,9 +1358,10 @@
   """
   return (e for e in entries if e.applied_visibility in visibilities)
 
-def remove_synthetic_or_fwk_only(entries):
+def remove_hal_non_visible(entries):
   """
-  Filter the given entries by removing those that are synthetic or fwk_only.
+  Filter the given entries by removing those that are not HAL visible:
+  synthetic, fwk_only, or fwk_java_public.
 
   Args:
     entries: An iterable of Entry nodes
@@ -1367,7 +1369,8 @@
   Yields:
     An iterable of Entry nodes
   """
-  return (e for e in entries if not (e.synthetic or e.visibility == 'fwk_only'))
+  return (e for e in entries if not (e.synthetic or e.visibility == 'fwk_only'
+                                     or e.visibility == 'fwk_java_public'))
 
 """
   Return the vndk version for a given hal minor version. The major version is assumed to be 3
@@ -1466,7 +1469,7 @@
   """
   ret = 0
   for sec in find_all_sections(root):
-      ret += len(list(filter_has_permission_needed(remove_synthetic_or_fwk_only(find_unique_entries(sec)))))
+      ret += len(list(filter_has_permission_needed(remove_hal_non_visible(find_unique_entries(sec)))))
 
   return ret
 
@@ -1585,7 +1588,7 @@
   for section in all_sections:
     min_major_version = None
     min_minor_version = None
-    for entry in remove_synthetic_or_fwk_only(find_unique_entries(section)):
+    for entry in remove_hal_non_visible(find_unique_entries(section)):
       min_major_version = (min_major_version or entry.hal_major_version)
       min_minor_version = (min_minor_version or entry.hal_minor_version)
       if entry.hal_major_version < min_major_version or \
diff --git a/camera/docs/metadata_model.py b/camera/docs/metadata_model.py
index 31a8c74..4428c90 100644
--- a/camera/docs/metadata_model.py
+++ b/camera/docs/metadata_model.py
@@ -977,7 +977,8 @@
     id: An optional numeric string, e.g. '0' or '0xFF'
     deprecated: A boolean, True if the enum should be deprecated.
     optional: A boolean
-    visibility: A string, one of "system", "java_public", "ndk_public", "hidden", "public"
+    visibility: A string, one of "system", "java_public", "ndk_public", "hidden", "public",
+                "fwk_java_public"
     notes: A string describing the notes, or None.
     sdk_notes: A string describing extra notes for public SDK only
     ndk_notes: A string describing extra notes for public NDK only
@@ -1032,7 +1033,8 @@
     parent_enum = None
     if (self.parent is not None and self.parent.parent is not None):
       parent_enum = self.parent.parent
-    if parent_enum is not None and parent_enum.visibility == 'fwk_only' or self._visibility == 'fwk_only':
+    if parent_enum is not None and parent_enum.visibility in ('fwk_only', 'fwk_java_public') \
+        or self._visibility in ('fwk_only', 'fwk_java_public'):
       return ','
     return ', // HIDL v' + str(self._hal_major_version) + '.' + str(self.hal_minor_version)
 
@@ -1249,8 +1251,8 @@
 
   @property
   def hidl_comment_string(self):
-    if self._visibility == 'fwk_only':
-      return 'fwk_only'
+    if self._visibility in ('fwk_only', 'fwk_java_public'):
+      return self._visibility
     visibility_lj = str(self.applied_visibility).ljust(12)
     return visibility_lj + ' | HIDL v' + str(self._hal_major_version) + '.' + str(self._hal_minor_version)
 
diff --git a/camera/docs/ndk_camera_metadata_tags.mako b/camera/docs/ndk_camera_metadata_tags.mako
index c079820..c2bb859 100644
--- a/camera/docs/ndk_camera_metadata_tags.mako
+++ b/camera/docs/ndk_camera_metadata_tags.mako
@@ -79,12 +79,12 @@
 typedef enum acamera_metadata_tag {
     % for sec in find_all_sections(metadata):
 <%
-      entries = remove_synthetic_or_fwk_only(find_unique_entries(sec))
+      entries = remove_hal_non_visible(find_unique_entries(sec))
       skip_sec = all(e.applied_ndk_visible == "false" for e in entries)
       if skip_sec:
         continue
 %>\
-      % for idx,entry in enumerate(remove_synthetic_or_fwk_only(find_unique_entries(sec))):
+      % for idx,entry in enumerate(remove_hal_non_visible(find_unique_entries(sec))):
         % if entry.applied_ndk_visible == "true":
           % if entry.deprecated:
     ${ndk(entry.name) + " = " | csym,ljust(60)}// Deprecated! DO NOT USE
@@ -134,7 +134,7 @@
  */
 
 % for sec in find_all_sections(metadata):
-  % for entry in filter_ndk_visible(remove_synthetic_or_fwk_only(find_unique_entries(sec))):
+  % for entry in filter_ndk_visible(remove_hal_non_visible(find_unique_entries(sec))):
     % if entry.enum:
 // ${ndk(entry.name) | csym}
 typedef enum acamera_metadata_enum_${csym(ndk(entry.name)).lower()} {
diff --git a/camera/include/system/camera_metadata_tags.h b/camera/include/system/camera_metadata_tags.h
index 7bea4a8..8079d79 100644
--- a/camera/include/system/camera_metadata_tags.h
+++ b/camera/include/system/camera_metadata_tags.h
@@ -382,6 +382,7 @@
     ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION,// int32[]      | system       | HIDL v3.6
     ANDROID_SENSOR_PIXEL_MODE,                        // enum         | public       | HIDL v3.6
     ANDROID_SENSOR_RAW_BINNING_FACTOR_USED,           // enum         | public       | HIDL v3.6
+    ANDROID_SENSOR_READOUT_TIMESTAMP,                 // enum         | fwk_java_public
     ANDROID_SENSOR_END,
 
     ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE =           // int32[]      | public       | HIDL v3.2
@@ -1100,6 +1101,12 @@
     ANDROID_SENSOR_RAW_BINNING_FACTOR_USED_FALSE                    , // HIDL v3.6
 } camera_metadata_enum_android_sensor_raw_binning_factor_used_t;
 
+// ANDROID_SENSOR_READOUT_TIMESTAMP
+typedef enum camera_metadata_enum_android_sensor_readout_timestamp {
+    ANDROID_SENSOR_READOUT_TIMESTAMP_NOT_SUPPORTED                  ,
+    ANDROID_SENSOR_READOUT_TIMESTAMP_HARDWARE                       ,
+} camera_metadata_enum_android_sensor_readout_timestamp_t;
+
 
 // ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
 typedef enum camera_metadata_enum_android_sensor_info_color_filter_arrangement {
diff --git a/camera/src/camera_metadata_tag_info.c b/camera/src/camera_metadata_tag_info.c
index 4a652aa..1ca265a 100644
--- a/camera/src/camera_metadata_tag_info.c
+++ b/camera/src/camera_metadata_tag_info.c
@@ -617,6 +617,8 @@
     { "pixelMode",                     TYPE_BYTE   },
     [ ANDROID_SENSOR_RAW_BINNING_FACTOR_USED - ANDROID_SENSOR_START ] =
     { "rawBinningFactorUsed",          TYPE_BYTE   },
+    [ ANDROID_SENSOR_READOUT_TIMESTAMP - ANDROID_SENSOR_START ] =
+    { "readoutTimestamp",              TYPE_BYTE   },
 };
 
 static tag_info_t android_sensor_info[ANDROID_SENSOR_INFO_END -
@@ -2901,6 +2903,21 @@
             }
             break;
         }
+        case ANDROID_SENSOR_READOUT_TIMESTAMP: {
+            switch (value) {
+                case ANDROID_SENSOR_READOUT_TIMESTAMP_NOT_SUPPORTED:
+                    msg = "NOT_SUPPORTED";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_READOUT_TIMESTAMP_HARDWARE:
+                    msg = "HARDWARE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
 
         case ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE: {
             break;
@@ -6009,6 +6026,21 @@
                 }
             break;
         }
+        case ANDROID_SENSOR_READOUT_TIMESTAMP: {
+                enumName = "NOT_SUPPORTED";
+                if (strncmp(name, enumName, size) == 0) {
+                    *value = ANDROID_SENSOR_READOUT_TIMESTAMP_NOT_SUPPORTED;
+                    ret = 0;
+                    break;
+                }
+                enumName = "HARDWARE";
+                if (strncmp(name, enumName, size) == 0) {
+                    *value = ANDROID_SENSOR_READOUT_TIMESTAMP_HARDWARE;
+                    ret = 0;
+                    break;
+                }
+            break;
+        }
 
         case ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE: {
             break;