Home | History | Annotate | Download | only in audiofx
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.media.audiofx;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.os.Handler;
     22 import android.os.Looper;
     23 import android.os.Message;
     24 import android.util.Log;
     25 import java.io.IOException;
     26 import java.lang.ref.WeakReference;
     27 import java.nio.ByteOrder;
     28 import java.nio.ByteBuffer;
     29 import java.util.UUID;
     30 
     31 /**
     32  * AudioEffect is the base class for controlling audio effects provided by the android audio
     33  * framework.
     34  * <p>Applications should not use the AudioEffect class directly but one of its derived classes to
     35  * control specific effects:
     36  * <ul>
     37  *   <li> {@link android.media.audiofx.Equalizer}</li>
     38  *   <li> {@link android.media.audiofx.Virtualizer}</li>
     39  *   <li> {@link android.media.audiofx.BassBoost}</li>
     40  *   <li> {@link android.media.audiofx.PresetReverb}</li>
     41  *   <li> {@link android.media.audiofx.EnvironmentalReverb}</li>
     42  * </ul>
     43  * <p>To apply the audio effect to a specific AudioTrack or MediaPlayer instance,
     44  * the application must specify the audio session ID of that instance when creating the AudioEffect.
     45  * (see {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions).
     46  * <p>NOTE: attaching insert effects (equalizer, bass boost, virtualizer) to the global audio output
     47  * mix by use of session 0 is deprecated.
     48  * <p>Creating an AudioEffect object will create the corresponding effect engine in the audio
     49  * framework if no instance of the same effect type exists in the specified audio session.
     50  * If one exists, this instance will be used.
     51  * <p>The application creating the AudioEffect object (or a derived class) will either receive
     52  * control of the effect engine or not depending on the priority parameter. If priority is higher
     53  * than the priority used by the current effect engine owner, the control will be transfered to the
     54  * new object. Otherwise control will remain with the previous object. In this case, the new
     55  * application will be notified of changes in effect engine state or control ownership by the
     56  * appropiate listener.
     57  */
     58 
     59 public class AudioEffect {
     60     static {
     61         System.loadLibrary("audioeffect_jni");
     62         native_init();
     63     }
     64 
     65     private final static String TAG = "AudioEffect-JAVA";
     66 
     67     // effect type UUIDs are taken from hardware/libhardware/include/hardware/audio_effect.h
     68 
     69     /**
     70      * The following UUIDs define effect types corresponding to standard audio
     71      * effects whose implementation and interface conform to the OpenSL ES
     72      * specification. The definitions match the corresponding interface IDs in
     73      * OpenSLES_IID.h
     74      */
     75 
     76     /**
     77      * UUID for environmental reverb effect
     78      * @hide
     79      */
     80     public static final UUID EFFECT_TYPE_ENV_REVERB = UUID
     81             .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
     82     /**
     83      * UUID for preset reverb effect
     84      * @hide
     85      */
     86     public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID
     87             .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
     88     /**
     89      * UUID for equalizer effect
     90      * @hide
     91      */
     92     public static final UUID EFFECT_TYPE_EQUALIZER = UUID
     93             .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
     94     /**
     95      * UUID for bass boost effect
     96      * @hide
     97      */
     98     public static final UUID EFFECT_TYPE_BASS_BOOST = UUID
     99             .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
    100     /**
    101      * UUID for virtualizer effect
    102      * @hide
    103      */
    104     public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID
    105             .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
    106 
    107     /**
    108      * UUID for Automatic Gain Control (AGC) audio pre-processing
    109      * @hide
    110      */
    111     public static final UUID EFFECT_TYPE_AGC = UUID
    112             .fromString("0a8abfe0-654c-11e0-ba26-0002a5d5c51b");
    113 
    114     /**
    115      * UUID for Acoustic Echo Canceler (AEC) audio pre-processing
    116      * @hide
    117      */
    118     public static final UUID EFFECT_TYPE_AEC = UUID
    119             .fromString("7b491460-8d4d-11e0-bd61-0002a5d5c51b");
    120 
    121     /**
    122      * UUID for Noise Suppressor (NS) audio pre-processing
    123      * @hide
    124      */
    125     public static final UUID EFFECT_TYPE_NS = UUID
    126             .fromString("58b4b260-8e06-11e0-aa8e-0002a5d5c51b");
    127 
    128     /**
    129      * Null effect UUID. Used when the UUID for effect type of
    130      * @hide
    131      */
    132     public static final UUID EFFECT_TYPE_NULL = UUID
    133             .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
    134 
    135     /**
    136      * State of an AudioEffect object that was not successfully initialized upon
    137      * creation
    138      * @hide
    139      */
    140     public static final int STATE_UNINITIALIZED = 0;
    141     /**
    142      * State of an AudioEffect object that is ready to be used.
    143      * @hide
    144      */
    145     public static final int STATE_INITIALIZED = 1;
    146 
    147     // to keep in sync with
    148     // frameworks/base/include/media/AudioEffect.h
    149     /**
    150      * Event id for engine control ownership change notification.
    151      * @hide
    152      */
    153     public static final int NATIVE_EVENT_CONTROL_STATUS = 0;
    154     /**
    155      * Event id for engine state change notification.
    156      * @hide
    157      */
    158     public static final int NATIVE_EVENT_ENABLED_STATUS = 1;
    159     /**
    160      * Event id for engine parameter change notification.
    161      * @hide
    162      */
    163     public static final int NATIVE_EVENT_PARAMETER_CHANGED = 2;
    164 
    165     /**
    166      * Successful operation.
    167      */
    168     public static final int SUCCESS = 0;
    169     /**
    170      * Unspecified error.
    171      */
    172     public static final int ERROR = -1;
    173     /**
    174      * Internal operation status. Not returned by any method.
    175      */
    176     public static final int ALREADY_EXISTS = -2;
    177     /**
    178      * Operation failed due to bad object initialization.
    179      */
    180     public static final int ERROR_NO_INIT = -3;
    181     /**
    182      * Operation failed due to bad parameter value.
    183      */
    184     public static final int ERROR_BAD_VALUE = -4;
    185     /**
    186      * Operation failed because it was requested in wrong state.
    187      */
    188     public static final int ERROR_INVALID_OPERATION = -5;
    189     /**
    190      * Operation failed due to lack of memory.
    191      */
    192     public static final int ERROR_NO_MEMORY = -6;
    193     /**
    194      * Operation failed due to dead remote object.
    195      */
    196     public static final int ERROR_DEAD_OBJECT = -7;
    197 
    198     /**
    199      * The effect descriptor contains information on a particular effect implemented in the
    200      * audio framework:<br>
    201      * <ul>
    202      *  <li>type: UUID corresponding to the OpenSL ES interface implemented by this effect</li>
    203      *  <li>uuid: UUID for this particular implementation</li>
    204      *  <li>connectMode: {@link #EFFECT_INSERT}, {@link #EFFECT_AUXILIARY} or
    205      *  {at_link #EFFECT_PRE_PROCESSING}</li>
    206      *  <li>name: human readable effect name</li>
    207      *  <li>implementor: human readable effect implementor name</li>
    208      * </ul>
    209      * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects
    210      * enumeration.
    211      */
    212     public static class Descriptor {
    213 
    214         public Descriptor() {
    215         }
    216 
    217         public Descriptor(String type, String uuid, String connectMode,
    218                 String name, String implementor) {
    219             this.type = UUID.fromString(type);
    220             this.uuid = UUID.fromString(uuid);
    221             this.connectMode = connectMode;
    222             this.name = name;
    223             this.implementor = implementor;
    224         }
    225 
    226         /**
    227          *  Indicates the generic type of the effect (Equalizer, Bass boost ...). The UUID
    228          *  corresponds to the OpenSL ES Interface ID for this type of effect.
    229          */
    230         public UUID type;
    231         /**
    232          *  Indicates the particular implementation of the effect in that type. Several effects
    233          *  can have the same type but this uuid is unique to a given implementation.
    234          */
    235         public UUID uuid;
    236         /**
    237          *  Indicates if the effect is of insert category {@link #EFFECT_INSERT}, auxiliary
    238          *  category {@link #EFFECT_AUXILIARY} or pre processing category
    239          *  {at_link #EFFECT_PRE_PROCESSING}. Insert effects (Typically an Equalizer) are applied
    240          *  to the entire audio source and usually not shared by several sources. Auxiliary effects
    241          *  (typically a reverberator) are applied to part of the signal (wet) and the effect output
    242          *  is added to the original signal (dry).
    243          *  Audio pre processing are applied to audio captured on a particular AudioRecord.
    244          */
    245         public String connectMode;
    246         /**
    247          * Human readable effect name
    248          */
    249         public String name;
    250         /**
    251          * Human readable effect implementor name
    252          */
    253         public String implementor;
    254     };
    255 
    256     /**
    257      * Effect connection mode is insert. Specifying an audio session ID when creating the effect
    258      * will insert this effect after all players in the same audio session.
    259      */
    260     public static final String EFFECT_INSERT = "Insert";
    261     /**
    262      * Effect connection mode is auxiliary.
    263      * <p>Auxiliary effects must be created on session 0 (global output mix). In order for a
    264      * MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to
    265      * this effect and a send level must be specified.
    266      * <p>Use the effect ID returned by {@link #getId()} to designate this particular effect when
    267      * attaching it to the MediaPlayer or AudioTrack.
    268      */
    269     public static final String EFFECT_AUXILIARY = "Auxiliary";
    270     /**
    271      * Effect connection mode is pre processing.
    272      * The audio pre processing effects are attached to an audio input (AudioRecord).
    273      * @hide
    274      */
    275     public static final String EFFECT_PRE_PROCESSING = "Pre Processing";
    276 
    277     // --------------------------------------------------------------------------
    278     // Member variables
    279     // --------------------
    280     /**
    281      * Indicates the state of the AudioEffect instance
    282      */
    283     private int mState = STATE_UNINITIALIZED;
    284     /**
    285      * Lock to synchronize access to mState
    286      */
    287     private final Object mStateLock = new Object();
    288     /**
    289      * System wide unique effect ID
    290      */
    291     private int mId;
    292 
    293     // accessed by native methods
    294     private int mNativeAudioEffect;
    295     private int mJniData;
    296 
    297     /**
    298      * Effect descriptor
    299      */
    300     private Descriptor mDescriptor;
    301 
    302     /**
    303      * Listener for effect engine state change notifications.
    304      *
    305      * @see #setEnableStatusListener(OnEnableStatusChangeListener)
    306      */
    307     private OnEnableStatusChangeListener mEnableStatusChangeListener = null;
    308     /**
    309      * Listener for effect engine control ownership change notifications.
    310      *
    311      * @see #setControlStatusListener(OnControlStatusChangeListener)
    312      */
    313     private OnControlStatusChangeListener mControlChangeStatusListener = null;
    314     /**
    315      * Listener for effect engine control ownership change notifications.
    316      *
    317      * @see #setParameterListener(OnParameterChangeListener)
    318      */
    319     private OnParameterChangeListener mParameterChangeListener = null;
    320     /**
    321      * Lock to protect listeners updates against event notifications
    322      * @hide
    323      */
    324     public final Object mListenerLock = new Object();
    325     /**
    326      * Handler for events coming from the native code
    327      * @hide
    328      */
    329     public NativeEventHandler mNativeEventHandler = null;
    330 
    331     // --------------------------------------------------------------------------
    332     // Constructor, Finalize
    333     // --------------------
    334     /**
    335      * Class constructor.
    336      *
    337      * @param type type of effect engine created. See {@link #EFFECT_TYPE_ENV_REVERB},
    338      *            {@link #EFFECT_TYPE_EQUALIZER} ... Types corresponding to
    339      *            built-in effects are defined by AudioEffect class. Other types
    340      *            can be specified provided they correspond an existing OpenSL
    341      *            ES interface ID and the corresponsing effect is available on
    342      *            the platform. If an unspecified effect type is requested, the
    343      *            constructor with throw the IllegalArgumentException. This
    344      *            parameter can be set to {@link #EFFECT_TYPE_NULL} in which
    345      *            case only the uuid will be used to select the effect.
    346      * @param uuid unique identifier of a particular effect implementation.
    347      *            Must be specified if the caller wants to use a particular
    348      *            implementation of an effect type. This parameter can be set to
    349      *            {@link #EFFECT_TYPE_NULL} in which case only the type will
    350      *            be used to select the effect.
    351      * @param priority the priority level requested by the application for
    352      *            controlling the effect engine. As the same effect engine can
    353      *            be shared by several applications, this parameter indicates
    354      *            how much the requesting application needs control of effect
    355      *            parameters. The normal priority is 0, above normal is a
    356      *            positive number, below normal a negative number.
    357      * @param audioSession system wide unique audio session identifier.
    358      *            The effect will be attached to the MediaPlayer or AudioTrack in
    359      *            the same audio session.
    360      *
    361      * @throws java.lang.IllegalArgumentException
    362      * @throws java.lang.UnsupportedOperationException
    363      * @throws java.lang.RuntimeException
    364      * @hide
    365      */
    366 
    367     public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
    368             throws IllegalArgumentException, UnsupportedOperationException,
    369             RuntimeException {
    370         int[] id = new int[1];
    371         Descriptor[] desc = new Descriptor[1];
    372         // native initialization
    373         int initResult = native_setup(new WeakReference<AudioEffect>(this),
    374                 type.toString(), uuid.toString(), priority, audioSession, id,
    375                 desc);
    376         if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
    377             Log.e(TAG, "Error code " + initResult
    378                     + " when initializing AudioEffect.");
    379             switch (initResult) {
    380             case ERROR_BAD_VALUE:
    381                 throw (new IllegalArgumentException("Effect type: " + type
    382                         + " not supported."));
    383             case ERROR_INVALID_OPERATION:
    384                 throw (new UnsupportedOperationException(
    385                         "Effect library not loaded"));
    386             default:
    387                 throw (new RuntimeException(
    388                         "Cannot initialize effect engine for type: " + type
    389                                 + "Error: " + initResult));
    390             }
    391         }
    392         mId = id[0];
    393         mDescriptor = desc[0];
    394         synchronized (mStateLock) {
    395             mState = STATE_INITIALIZED;
    396         }
    397     }
    398 
    399     /**
    400      * Releases the native AudioEffect resources. It is a good practice to
    401      * release the effect engine when not in use as control can be returned to
    402      * other applications or the native resources released.
    403      */
    404     public void release() {
    405         synchronized (mStateLock) {
    406             native_release();
    407             mState = STATE_UNINITIALIZED;
    408         }
    409     }
    410 
    411     @Override
    412     protected void finalize() {
    413         native_finalize();
    414     }
    415 
    416     /**
    417      * Get the effect descriptor.
    418      *
    419      * @see android.media.audiofx.AudioEffect.Descriptor
    420      * @throws IllegalStateException
    421      */
    422     public Descriptor getDescriptor() throws IllegalStateException {
    423         checkState("getDescriptor()");
    424         return mDescriptor;
    425     }
    426 
    427     // --------------------------------------------------------------------------
    428     // Effects Enumeration
    429     // --------------------
    430 
    431     /**
    432      * Query all effects available on the platform. Returns an array of
    433      * {@link android.media.audiofx.AudioEffect.Descriptor} objects
    434      *
    435      * @throws IllegalStateException
    436      */
    437 
    438     static public Descriptor[] queryEffects() {
    439         return (Descriptor[]) native_query_effects();
    440     }
    441 
    442     /**
    443      * Query all audio pre processing effects applied to the AudioRecord with the supplied
    444      * audio session ID. Returns an array of {@link android.media.audiofx.AudioEffect.Descriptor}
    445      * objects.
    446      * @param audioSession system wide unique audio session identifier.
    447      * @throws IllegalStateException
    448      * @hide
    449      */
    450 
    451     static public Descriptor[] queryPreProcessings(int audioSession) {
    452         return (Descriptor[]) native_query_pre_processing(audioSession);
    453     }
    454 
    455     // --------------------------------------------------------------------------
    456     // Control methods
    457     // --------------------
    458 
    459     /**
    460      * Enable or disable the effect.
    461      * Creating an audio effect does not automatically apply this effect on the audio source. It
    462      * creates the resources necessary to process this effect but the audio signal is still bypassed
    463      * through the effect engine. Calling this method will make that the effect is actually applied
    464      * or not to the audio content being played in the corresponding audio session.
    465      *
    466      * @param enabled the requested enable state
    467      * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
    468      *         or {@link #ERROR_DEAD_OBJECT} in case of failure.
    469      * @throws IllegalStateException
    470      */
    471     public int setEnabled(boolean enabled) throws IllegalStateException {
    472         checkState("setEnabled()");
    473         return native_setEnabled(enabled);
    474     }
    475 
    476     /**
    477      * Set effect parameter. The setParameter method is provided in several
    478      * forms addressing most common parameter formats. This form is the most
    479      * generic one where the parameter and its value are both specified as an
    480      * array of bytes. The parameter and value type and length are therefore
    481      * totally free. For standard effect defined by OpenSL ES, the parameter
    482      * format and values must match the definitions in the corresponding OpenSL
    483      * ES interface.
    484      *
    485      * @param param the identifier of the parameter to set
    486      * @param value the new value for the specified parameter
    487      * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
    488      *         {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
    489      *         {@link #ERROR_DEAD_OBJECT} in case of failure
    490      * @throws IllegalStateException
    491      * @hide
    492      */
    493     public int setParameter(byte[] param, byte[] value)
    494             throws IllegalStateException {
    495         checkState("setParameter()");
    496         return native_setParameter(param.length, param, value.length, value);
    497     }
    498 
    499     /**
    500      * Set effect parameter. The parameter and its value are integers.
    501      *
    502      * @see #setParameter(byte[], byte[])
    503      * @hide
    504      */
    505     public int setParameter(int param, int value) throws IllegalStateException {
    506         byte[] p = intToByteArray(param);
    507         byte[] v = intToByteArray(value);
    508         return setParameter(p, v);
    509     }
    510 
    511     /**
    512      * Set effect parameter. The parameter is an integer and the value is a
    513      * short integer.
    514      *
    515      * @see #setParameter(byte[], byte[])
    516      * @hide
    517      */
    518     public int setParameter(int param, short value)
    519             throws IllegalStateException {
    520         byte[] p = intToByteArray(param);
    521         byte[] v = shortToByteArray(value);
    522         return setParameter(p, v);
    523     }
    524 
    525     /**
    526      * Set effect parameter. The parameter is an integer and the value is an
    527      * array of bytes.
    528      *
    529      * @see #setParameter(byte[], byte[])
    530      * @hide
    531      */
    532     public int setParameter(int param, byte[] value)
    533             throws IllegalStateException {
    534         byte[] p = intToByteArray(param);
    535         return setParameter(p, value);
    536     }
    537 
    538     /**
    539      * Set effect parameter. The parameter is an array of 1 or 2 integers and
    540      * the value is also an array of 1 or 2 integers
    541      *
    542      * @see #setParameter(byte[], byte[])
    543      * @hide
    544      */
    545     public int setParameter(int[] param, int[] value)
    546             throws IllegalStateException {
    547         if (param.length > 2 || value.length > 2) {
    548             return ERROR_BAD_VALUE;
    549         }
    550         byte[] p = intToByteArray(param[0]);
    551         if (param.length > 1) {
    552             byte[] p2 = intToByteArray(param[1]);
    553             p = concatArrays(p, p2);
    554         }
    555         byte[] v = intToByteArray(value[0]);
    556         if (value.length > 1) {
    557             byte[] v2 = intToByteArray(value[1]);
    558             v = concatArrays(v, v2);
    559         }
    560         return setParameter(p, v);
    561     }
    562 
    563     /**
    564      * Set effect parameter. The parameter is an array of 1 or 2 integers and
    565      * the value is an array of 1 or 2 short integers
    566      *
    567      * @see #setParameter(byte[], byte[])
    568      * @hide
    569      */
    570     public int setParameter(int[] param, short[] value)
    571             throws IllegalStateException {
    572         if (param.length > 2 || value.length > 2) {
    573             return ERROR_BAD_VALUE;
    574         }
    575         byte[] p = intToByteArray(param[0]);
    576         if (param.length > 1) {
    577             byte[] p2 = intToByteArray(param[1]);
    578             p = concatArrays(p, p2);
    579         }
    580 
    581         byte[] v = shortToByteArray(value[0]);
    582         if (value.length > 1) {
    583             byte[] v2 = shortToByteArray(value[1]);
    584             v = concatArrays(v, v2);
    585         }
    586         return setParameter(p, v);
    587     }
    588 
    589     /**
    590      * Set effect parameter. The parameter is an array of 1 or 2 integers and
    591      * the value is an array of bytes
    592      *
    593      * @see #setParameter(byte[], byte[])
    594      * @hide
    595      */
    596     public int setParameter(int[] param, byte[] value)
    597             throws IllegalStateException {
    598         if (param.length > 2) {
    599             return ERROR_BAD_VALUE;
    600         }
    601         byte[] p = intToByteArray(param[0]);
    602         if (param.length > 1) {
    603             byte[] p2 = intToByteArray(param[1]);
    604             p = concatArrays(p, p2);
    605         }
    606         return setParameter(p, value);
    607     }
    608 
    609     /**
    610      * Get effect parameter. The getParameter method is provided in several
    611      * forms addressing most common parameter formats. This form is the most
    612      * generic one where the parameter and its value are both specified as an
    613      * array of bytes. The parameter and value type and length are therefore
    614      * totally free.
    615      *
    616      * @param param the identifier of the parameter to set
    617      * @param value the new value for the specified parameter
    618      * @return the number of meaningful bytes in value array in case of success or
    619      *  {@link #ERROR_BAD_VALUE}, {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION}
    620      *  or {@link #ERROR_DEAD_OBJECT} in case of failure.
    621      * @throws IllegalStateException
    622      * @hide
    623      */
    624     public int getParameter(byte[] param, byte[] value)
    625             throws IllegalStateException {
    626         checkState("getParameter()");
    627         return native_getParameter(param.length, param, value.length, value);
    628     }
    629 
    630     /**
    631      * Get effect parameter. The parameter is an integer and the value is an
    632      * array of bytes.
    633      *
    634      * @see #getParameter(byte[], byte[])
    635      * @hide
    636      */
    637     public int getParameter(int param, byte[] value)
    638             throws IllegalStateException {
    639         byte[] p = intToByteArray(param);
    640 
    641         return getParameter(p, value);
    642     }
    643 
    644     /**
    645      * Get effect parameter. The parameter is an integer and the value is an
    646      * array of 1 or 2 integers
    647      *
    648      * @see #getParameter(byte[], byte[])
    649      * In case of success, returns the number of meaningful integers in value array.
    650      * @hide
    651      */
    652     public int getParameter(int param, int[] value)
    653             throws IllegalStateException {
    654         if (value.length > 2) {
    655             return ERROR_BAD_VALUE;
    656         }
    657         byte[] p = intToByteArray(param);
    658 
    659         byte[] v = new byte[value.length * 4];
    660 
    661         int status = getParameter(p, v);
    662 
    663         if (status == 4 || status == 8) {
    664             value[0] = byteArrayToInt(v);
    665             if (status == 8) {
    666                 value[1] = byteArrayToInt(v, 4);
    667             }
    668             status /= 4;
    669         } else {
    670             status = ERROR;
    671         }
    672         return status;
    673     }
    674 
    675     /**
    676      * Get effect parameter. The parameter is an integer and the value is an
    677      * array of 1 or 2 short integers
    678      *
    679      * @see #getParameter(byte[], byte[])
    680      * In case of success, returns the number of meaningful short integers in value array.
    681      * @hide
    682      */
    683     public int getParameter(int param, short[] value)
    684             throws IllegalStateException {
    685         if (value.length > 2) {
    686             return ERROR_BAD_VALUE;
    687         }
    688         byte[] p = intToByteArray(param);
    689 
    690         byte[] v = new byte[value.length * 2];
    691 
    692         int status = getParameter(p, v);
    693 
    694         if (status == 2 || status == 4) {
    695             value[0] = byteArrayToShort(v);
    696             if (status == 4) {
    697                 value[1] = byteArrayToShort(v, 2);
    698             }
    699             status /= 2;
    700         } else {
    701             status = ERROR;
    702         }
    703         return status;
    704     }
    705 
    706     /**
    707      * Get effect parameter. The parameter is an array of 1 or 2 integers and
    708      * the value is also an array of 1 or 2 integers
    709      *
    710      * @see #getParameter(byte[], byte[])
    711      * In case of success, the returns the number of meaningful integers in value array.
    712      * @hide
    713      */
    714     public int getParameter(int[] param, int[] value)
    715             throws IllegalStateException {
    716         if (param.length > 2 || value.length > 2) {
    717             return ERROR_BAD_VALUE;
    718         }
    719         byte[] p = intToByteArray(param[0]);
    720         if (param.length > 1) {
    721             byte[] p2 = intToByteArray(param[1]);
    722             p = concatArrays(p, p2);
    723         }
    724         byte[] v = new byte[value.length * 4];
    725 
    726         int status = getParameter(p, v);
    727 
    728         if (status == 4 || status == 8) {
    729             value[0] = byteArrayToInt(v);
    730             if (status == 8) {
    731                 value[1] = byteArrayToInt(v, 4);
    732             }
    733             status /= 4;
    734         } else {
    735             status = ERROR;
    736         }
    737         return status;
    738     }
    739 
    740     /**
    741      * Get effect parameter. The parameter is an array of 1 or 2 integers and
    742      * the value is an array of 1 or 2 short integers
    743      *
    744      * @see #getParameter(byte[], byte[])
    745      * In case of success, returns the number of meaningful short integers in value array.
    746      * @hide
    747      */
    748     public int getParameter(int[] param, short[] value)
    749             throws IllegalStateException {
    750         if (param.length > 2 || value.length > 2) {
    751             return ERROR_BAD_VALUE;
    752         }
    753         byte[] p = intToByteArray(param[0]);
    754         if (param.length > 1) {
    755             byte[] p2 = intToByteArray(param[1]);
    756             p = concatArrays(p, p2);
    757         }
    758         byte[] v = new byte[value.length * 2];
    759 
    760         int status = getParameter(p, v);
    761 
    762         if (status == 2 || status == 4) {
    763             value[0] = byteArrayToShort(v);
    764             if (status == 4) {
    765                 value[1] = byteArrayToShort(v, 2);
    766             }
    767             status /= 2;
    768         } else {
    769             status = ERROR;
    770         }
    771         return status;
    772     }
    773 
    774     /**
    775      * Get effect parameter. The parameter is an array of 1 or 2 integers and
    776      * the value is an array of bytes
    777      *
    778      * @see #getParameter(byte[], byte[])
    779      * @hide
    780      */
    781     public int getParameter(int[] param, byte[] value)
    782             throws IllegalStateException {
    783         if (param.length > 2) {
    784             return ERROR_BAD_VALUE;
    785         }
    786         byte[] p = intToByteArray(param[0]);
    787         if (param.length > 1) {
    788             byte[] p2 = intToByteArray(param[1]);
    789             p = concatArrays(p, p2);
    790         }
    791 
    792         return getParameter(p, value);
    793     }
    794 
    795     /**
    796      * Send a command to the effect engine. This method is intended to send
    797      * proprietary commands to a particular effect implementation.
    798      * In case of success, returns the number of meaningful bytes in reply array.
    799      * In case of failure, the returned value is negative and implementation specific.
    800      * @hide
    801      */
    802     public int command(int cmdCode, byte[] command, byte[] reply)
    803             throws IllegalStateException {
    804         checkState("command()");
    805         return native_command(cmdCode, command.length, command, reply.length, reply);
    806     }
    807 
    808     // --------------------------------------------------------------------------
    809     // Getters
    810     // --------------------
    811 
    812     /**
    813      * Returns effect unique identifier. This system wide unique identifier can
    814      * be used to attach this effect to a MediaPlayer or an AudioTrack when the
    815      * effect is an auxiliary effect (Reverb)
    816      *
    817      * @return the effect identifier.
    818      * @throws IllegalStateException
    819      */
    820     public int getId() throws IllegalStateException {
    821         checkState("getId()");
    822         return mId;
    823     }
    824 
    825     /**
    826      * Returns effect enabled state
    827      *
    828      * @return true if the effect is enabled, false otherwise.
    829      * @throws IllegalStateException
    830      */
    831     public boolean getEnabled() throws IllegalStateException {
    832         checkState("getEnabled()");
    833         return native_getEnabled();
    834     }
    835 
    836     /**
    837      * Checks if this AudioEffect object is controlling the effect engine.
    838      *
    839      * @return true if this instance has control of effect engine, false
    840      *         otherwise.
    841      * @throws IllegalStateException
    842      */
    843     public boolean hasControl() throws IllegalStateException {
    844         checkState("hasControl()");
    845         return native_hasControl();
    846     }
    847 
    848     // --------------------------------------------------------------------------
    849     // Initialization / configuration
    850     // --------------------
    851     /**
    852      * Sets the listener AudioEffect notifies when the effect engine is enabled
    853      * or disabled.
    854      *
    855      * @param listener
    856      */
    857     public void setEnableStatusListener(OnEnableStatusChangeListener listener) {
    858         synchronized (mListenerLock) {
    859             mEnableStatusChangeListener = listener;
    860         }
    861         if ((listener != null) && (mNativeEventHandler == null)) {
    862             createNativeEventHandler();
    863         }
    864     }
    865 
    866     /**
    867      * Sets the listener AudioEffect notifies when the effect engine control is
    868      * taken or returned.
    869      *
    870      * @param listener
    871      */
    872     public void setControlStatusListener(OnControlStatusChangeListener listener) {
    873         synchronized (mListenerLock) {
    874             mControlChangeStatusListener = listener;
    875         }
    876         if ((listener != null) && (mNativeEventHandler == null)) {
    877             createNativeEventHandler();
    878         }
    879     }
    880 
    881     /**
    882      * Sets the listener AudioEffect notifies when a parameter is changed.
    883      *
    884      * @param listener
    885      * @hide
    886      */
    887     public void setParameterListener(OnParameterChangeListener listener) {
    888         synchronized (mListenerLock) {
    889             mParameterChangeListener = listener;
    890         }
    891         if ((listener != null) && (mNativeEventHandler == null)) {
    892             createNativeEventHandler();
    893         }
    894     }
    895 
    896     // Convenience method for the creation of the native event handler
    897     // It is called only when a non-null event listener is set.
    898     // precondition:
    899     // mNativeEventHandler is null
    900     private void createNativeEventHandler() {
    901         Looper looper;
    902         if ((looper = Looper.myLooper()) != null) {
    903             mNativeEventHandler = new NativeEventHandler(this, looper);
    904         } else if ((looper = Looper.getMainLooper()) != null) {
    905             mNativeEventHandler = new NativeEventHandler(this, looper);
    906         } else {
    907             mNativeEventHandler = null;
    908         }
    909     }
    910 
    911     // ---------------------------------------------------------
    912     // Interface definitions
    913     // --------------------
    914     /**
    915      * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect
    916      * when a the enabled state of the effect engine was changed by the controlling application.
    917      */
    918     public interface OnEnableStatusChangeListener {
    919         /**
    920          * Called on the listener to notify it that the effect engine has been
    921          * enabled or disabled.
    922          * @param effect the effect on which the interface is registered.
    923          * @param enabled new effect state.
    924          */
    925         void onEnableStatusChange(AudioEffect effect, boolean enabled);
    926     }
    927 
    928     /**
    929      * The OnControlStatusChangeListener interface defines a method called by the AudioEffect
    930      * when a the control of the effect engine is gained or lost by the application
    931      */
    932     public interface OnControlStatusChangeListener {
    933         /**
    934          * Called on the listener to notify it that the effect engine control
    935          * has been taken or returned.
    936          * @param effect the effect on which the interface is registered.
    937          * @param controlGranted true if the application has been granted control of the effect
    938          * engine, false otherwise.
    939          */
    940         void onControlStatusChange(AudioEffect effect, boolean controlGranted);
    941     }
    942 
    943     /**
    944      * The OnParameterChangeListener interface defines a method called by the AudioEffect
    945      * when a parameter is changed in the effect engine by the controlling application.
    946      * @hide
    947      */
    948     public interface OnParameterChangeListener {
    949         /**
    950          * Called on the listener to notify it that a parameter value has changed.
    951          * @param effect the effect on which the interface is registered.
    952          * @param status status of the set parameter operation.
    953          * @param param ID of the modified parameter.
    954          * @param value the new parameter value.
    955          */
    956         void onParameterChange(AudioEffect effect, int status, byte[] param,
    957                 byte[] value);
    958     }
    959 
    960 
    961     // -------------------------------------------------------------------------
    962     // Audio Effect Control panel intents
    963     // -------------------------------------------------------------------------
    964 
    965     /**
    966      *  Intent to launch an audio effect control panel UI.
    967      *  <p>The goal of this intent is to enable separate implementations of music/media player
    968      *  applications and audio effect control application or services.
    969      *  This will allow platform vendors to offer more advanced control options for standard effects
    970      *  or control for platform specific effects.
    971      *  <p>The intent carries a number of extras used by the player application to communicate
    972      *  necessary pieces of information to the control panel application.
    973      *  <p>The calling application must use the
    974      *  {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the
    975      *  control panel so that its package name is indicated and used by the control panel
    976      *  application to keep track of changes for this particular application.
    977      *  <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the
    978      *  audio effects should be applied. If no audio session is specified, either one of the
    979      *  follownig will happen:
    980      *  <p>- If an audio session was previously opened by the calling application with
    981      *  {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will
    982      *  be applied to that session.
    983      *  <p>- If no audio session is opened, the changes will be stored in the package specific
    984      *  storage area and applied whenever a new audio session is opened by this application.
    985      *  <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application
    986      *  customize both the UI layout and the default audio effect settings if none are already
    987      *  stored for the calling application.
    988      */
    989     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    990     public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL =
    991         "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL";
    992 
    993     /**
    994      *  Intent to signal to the effect control application or service that a new audio session
    995      *  is opened and requires audio effects to be applied.
    996      *  <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no
    997      *  UI should be displayed in this case. Music player applications can broadcast this intent
    998      *  before starting playback to make sure that any audio effect settings previously selected
    999      *  by the user are applied.
   1000      *  <p>The effect control application receiving this intent will look for previously stored
   1001      *  settings for the calling application, create all required audio effects and apply the
   1002      *  effect settings to the specified audio session.
   1003      *  <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
   1004      *  audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
   1005      *  <p>If no stored settings are found for the calling application, default settings for the
   1006      *  content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings
   1007      *  for a given content type are platform specific.
   1008      */
   1009     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
   1010     public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION =
   1011         "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION";
   1012 
   1013     /**
   1014      *  Intent to signal to the effect control application or service that an audio session
   1015      *  is closed and that effects should not be applied anymore.
   1016      *  <p>The effect control application receiving this intent will delete all effects on
   1017      *  this session and store current settings in package specific storage.
   1018      *  <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
   1019      *  audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
   1020      *  <p>It is good practice for applications to broadcast this intent when music playback stops
   1021      *  and/or when exiting to free system resources consumed by audio effect engines.
   1022      */
   1023     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
   1024     public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION =
   1025         "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION";
   1026 
   1027     /**
   1028      * Contains the ID of the audio session the effects should be applied to.
   1029      * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL},
   1030      * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
   1031      * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
   1032      * <p>The extra value is of type int and is the audio session ID.
   1033      *  @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions.
   1034      */
   1035      public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION";
   1036 
   1037     /**
   1038      * Contains the package name of the calling application.
   1039      * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
   1040      * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
   1041      * <p>The extra value is a string containing the full package name.
   1042      */
   1043     public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME";
   1044 
   1045     /**
   1046      * Indicates which type of content is played by the application.
   1047      * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and
   1048      * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents.
   1049      * <p>This information is used by the effect control application to customize UI and select
   1050      * appropriate default effect settings. The content type is one of the following:
   1051      * <ul>
   1052      *   <li>{@link #CONTENT_TYPE_MUSIC}</li>
   1053      *   <li>{@link #CONTENT_TYPE_MOVIE}</li>
   1054      *   <li>{@link #CONTENT_TYPE_GAME}</li>
   1055      *   <li>{@link #CONTENT_TYPE_VOICE}</li>
   1056      * </ul>
   1057      * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}.
   1058      */
   1059     public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE";
   1060 
   1061     /**
   1062      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music
   1063      */
   1064     public static final int  CONTENT_TYPE_MUSIC = 0;
   1065     /**
   1066      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie
   1067      */
   1068     public static final int  CONTENT_TYPE_MOVIE = 1;
   1069     /**
   1070      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio
   1071      */
   1072     public static final int  CONTENT_TYPE_GAME = 2;
   1073     /**
   1074      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio
   1075      */
   1076     public static final int  CONTENT_TYPE_VOICE = 3;
   1077 
   1078 
   1079     // ---------------------------------------------------------
   1080     // Inner classes
   1081     // --------------------
   1082     /**
   1083      * Helper class to handle the forwarding of native events to the appropriate
   1084      * listeners
   1085      */
   1086     private class NativeEventHandler extends Handler {
   1087         private AudioEffect mAudioEffect;
   1088 
   1089         public NativeEventHandler(AudioEffect ae, Looper looper) {
   1090             super(looper);
   1091             mAudioEffect = ae;
   1092         }
   1093 
   1094         @Override
   1095         public void handleMessage(Message msg) {
   1096             if (mAudioEffect == null) {
   1097                 return;
   1098             }
   1099             switch (msg.what) {
   1100             case NATIVE_EVENT_ENABLED_STATUS:
   1101                 OnEnableStatusChangeListener enableStatusChangeListener = null;
   1102                 synchronized (mListenerLock) {
   1103                     enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener;
   1104                 }
   1105                 if (enableStatusChangeListener != null) {
   1106                     enableStatusChangeListener.onEnableStatusChange(
   1107                             mAudioEffect, (boolean) (msg.arg1 != 0));
   1108                 }
   1109                 break;
   1110             case NATIVE_EVENT_CONTROL_STATUS:
   1111                 OnControlStatusChangeListener controlStatusChangeListener = null;
   1112                 synchronized (mListenerLock) {
   1113                     controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener;
   1114                 }
   1115                 if (controlStatusChangeListener != null) {
   1116                     controlStatusChangeListener.onControlStatusChange(
   1117                             mAudioEffect, (boolean) (msg.arg1 != 0));
   1118                 }
   1119                 break;
   1120             case NATIVE_EVENT_PARAMETER_CHANGED:
   1121                 OnParameterChangeListener parameterChangeListener = null;
   1122                 synchronized (mListenerLock) {
   1123                     parameterChangeListener = mAudioEffect.mParameterChangeListener;
   1124                 }
   1125                 if (parameterChangeListener != null) {
   1126                     // arg1 contains offset of parameter value from start of
   1127                     // byte array
   1128                     int vOffset = msg.arg1;
   1129                     byte[] p = (byte[]) msg.obj;
   1130                     // See effect_param_t in EffectApi.h for psize and vsize
   1131                     // fields offsets
   1132                     int status = byteArrayToInt(p, 0);
   1133                     int psize = byteArrayToInt(p, 4);
   1134                     int vsize = byteArrayToInt(p, 8);
   1135                     byte[] param = new byte[psize];
   1136                     byte[] value = new byte[vsize];
   1137                     System.arraycopy(p, 12, param, 0, psize);
   1138                     System.arraycopy(p, vOffset, value, 0, vsize);
   1139 
   1140                     parameterChangeListener.onParameterChange(mAudioEffect,
   1141                             status, param, value);
   1142                 }
   1143                 break;
   1144 
   1145             default:
   1146                 Log.e(TAG, "handleMessage() Unknown event type: " + msg.what);
   1147                 break;
   1148             }
   1149         }
   1150     }
   1151 
   1152     // ---------------------------------------------------------
   1153     // Java methods called from the native side
   1154     // --------------------
   1155     @SuppressWarnings("unused")
   1156     private static void postEventFromNative(Object effect_ref, int what,
   1157             int arg1, int arg2, Object obj) {
   1158         AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get();
   1159         if (effect == null) {
   1160             return;
   1161         }
   1162         if (effect.mNativeEventHandler != null) {
   1163             Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
   1164                     arg2, obj);
   1165             effect.mNativeEventHandler.sendMessage(m);
   1166         }
   1167 
   1168     }
   1169 
   1170     // ---------------------------------------------------------
   1171     // Native methods called from the Java side
   1172     // --------------------
   1173 
   1174     private static native final void native_init();
   1175 
   1176     private native final int native_setup(Object audioeffect_this, String type,
   1177             String uuid, int priority, int audioSession, int[] id, Object[] desc);
   1178 
   1179     private native final void native_finalize();
   1180 
   1181     private native final void native_release();
   1182 
   1183     private native final int native_setEnabled(boolean enabled);
   1184 
   1185     private native final boolean native_getEnabled();
   1186 
   1187     private native final boolean native_hasControl();
   1188 
   1189     private native final int native_setParameter(int psize, byte[] param,
   1190             int vsize, byte[] value);
   1191 
   1192     private native final int native_getParameter(int psize, byte[] param,
   1193             int vsize, byte[] value);
   1194 
   1195     private native final int native_command(int cmdCode, int cmdSize,
   1196             byte[] cmdData, int repSize, byte[] repData);
   1197 
   1198     private static native Object[] native_query_effects();
   1199 
   1200     private static native Object[] native_query_pre_processing(int audioSession);
   1201 
   1202     // ---------------------------------------------------------
   1203     // Utility methods
   1204     // ------------------
   1205 
   1206     /**
   1207     * @hide
   1208     */
   1209     public void checkState(String methodName) throws IllegalStateException {
   1210         synchronized (mStateLock) {
   1211             if (mState != STATE_INITIALIZED) {
   1212                 throw (new IllegalStateException(methodName
   1213                         + " called on uninitialized AudioEffect."));
   1214             }
   1215         }
   1216     }
   1217 
   1218     /**
   1219      * @hide
   1220      */
   1221     public void checkStatus(int status) {
   1222         if (isError(status)) {
   1223             switch (status) {
   1224             case AudioEffect.ERROR_BAD_VALUE:
   1225                 throw (new IllegalArgumentException(
   1226                         "AudioEffect: bad parameter value"));
   1227             case AudioEffect.ERROR_INVALID_OPERATION:
   1228                 throw (new UnsupportedOperationException(
   1229                         "AudioEffect: invalid parameter operation"));
   1230             default:
   1231                 throw (new RuntimeException("AudioEffect: set/get parameter error"));
   1232             }
   1233         }
   1234     }
   1235 
   1236     /**
   1237      * @hide
   1238      */
   1239     public static boolean isError(int status) {
   1240         return (status < 0);
   1241     }
   1242 
   1243     /**
   1244      * @hide
   1245      */
   1246     public int byteArrayToInt(byte[] valueBuf) {
   1247         return byteArrayToInt(valueBuf, 0);
   1248 
   1249     }
   1250 
   1251     /**
   1252      * @hide
   1253      */
   1254     public int byteArrayToInt(byte[] valueBuf, int offset) {
   1255         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
   1256         converter.order(ByteOrder.nativeOrder());
   1257         return converter.getInt(offset);
   1258 
   1259     }
   1260 
   1261     /**
   1262      * @hide
   1263      */
   1264     public byte[] intToByteArray(int value) {
   1265         ByteBuffer converter = ByteBuffer.allocate(4);
   1266         converter.order(ByteOrder.nativeOrder());
   1267         converter.putInt(value);
   1268         return converter.array();
   1269     }
   1270 
   1271     /**
   1272      * @hide
   1273      */
   1274     public short byteArrayToShort(byte[] valueBuf) {
   1275         return byteArrayToShort(valueBuf, 0);
   1276     }
   1277 
   1278     /**
   1279      * @hide
   1280      */
   1281     public short byteArrayToShort(byte[] valueBuf, int offset) {
   1282         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
   1283         converter.order(ByteOrder.nativeOrder());
   1284         return converter.getShort(offset);
   1285 
   1286     }
   1287 
   1288     /**
   1289      * @hide
   1290      */
   1291     public byte[] shortToByteArray(short value) {
   1292         ByteBuffer converter = ByteBuffer.allocate(2);
   1293         converter.order(ByteOrder.nativeOrder());
   1294         short sValue = (short) value;
   1295         converter.putShort(sValue);
   1296         return converter.array();
   1297     }
   1298 
   1299     /**
   1300      * @hide
   1301      */
   1302     public byte[] concatArrays(byte[]... arrays) {
   1303         int len = 0;
   1304         for (byte[] a : arrays) {
   1305             len += a.length;
   1306         }
   1307         byte[] b = new byte[len];
   1308 
   1309         int offs = 0;
   1310         for (byte[] a : arrays) {
   1311             System.arraycopy(a, 0, b, offs, a.length);
   1312             offs += a.length;
   1313         }
   1314         return b;
   1315     }
   1316 }
   1317