10abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent/* 20abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Copyright (C) 2014 The Android Open Source Project 30abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * 40abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Licensed under the Apache License, Version 2.0 (the "License"); 50abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * you may not use this file except in compliance with the License. 60abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * You may obtain a copy of the License at 70abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * 80abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * http://www.apache.org/licenses/LICENSE-2.0 90abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * 100abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Unless required by applicable law or agreed to in writing, software 110abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * distributed under the License is distributed on an "AS IS" BASIS, 120abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * See the License for the specific language governing permissions and 140abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * limitations under the License. 150abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 160abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 170abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurentpackage android.media; 180abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 190abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent/** 200abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * An audio port is a node of the audio framework or hardware that can be connected to or 210abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * disconnect from another audio node to create a specific audio routing configuration. 220abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Examples of audio ports are an output device (speaker) or an output mix (see AudioMixPort). 230abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * All attributes that are relevant for applications to make routing selection are decribed 240abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * in an AudioPort, in particular: 250abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * - possible channel mask configurations. 260abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * - audio format (PCM 16bit, PCM 24bit...) 270abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * - gain: a port can be associated with one or more gain controllers (see AudioGain). 280abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * 290abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * This object is always created by the framework and read only by applications. 300abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * A list of all audio port descriptors currently available for applications to control 310abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * is obtained by AudioManager.listAudioPorts(). 320abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * An application can obtain an AudioPortConfig for a valid configuration of this port 330abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * by calling AudioPort.buildConfig() and use this configuration 340abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * to create a connection between audio sinks and sources with AudioManager.connectAudioPatch() 350abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * 360abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * @hide 370abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 380abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurentpublic class AudioPort { 3910804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean private static final String TAG = "AudioPort"; 400abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 410abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 420abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * For use by the audio framework. 430abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 440abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public static final int ROLE_NONE = 0; 450abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 460abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * The audio port is a source (produces audio) 470abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 480abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public static final int ROLE_SOURCE = 1; 490abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 500abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * The audio port is a sink (consumes audio) 510abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 520abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public static final int ROLE_SINK = 2; 530abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 540abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 550abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * audio port type for use by audio framework implementation 560abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 570abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public static final int TYPE_NONE = 0; 580abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 590abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 600abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public static final int TYPE_DEVICE = 1; 610abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 620abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 630abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public static final int TYPE_SUBMIX = 2; 640abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 650abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 660abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public static final int TYPE_SESSION = 3; 670abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 680abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 690abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent AudioHandle mHandle; 7007cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood protected final int mRole; 7110804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean private final String mName; 720abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent private final int[] mSamplingRates; 730abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent private final int[] mChannelMasks; 74f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean private final int[] mChannelIndexMasks; 750abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent private final int[] mFormats; 760abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent private final AudioGain[] mGains; 770abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent private AudioPortConfig mActiveConfig; 780abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 7910804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean AudioPort(AudioHandle handle, int role, String name, 80f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean int[] samplingRates, int[] channelMasks, int[] channelIndexMasks, 810abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent int[] formats, AudioGain[] gains) { 8210804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean 830abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent mHandle = handle; 840abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent mRole = role; 8510804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean mName = name; 860abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent mSamplingRates = samplingRates; 870abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent mChannelMasks = channelMasks; 88f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean mChannelIndexMasks = channelIndexMasks; 890abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent mFormats = formats; 900abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent mGains = gains; 910abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 920abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 930abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent AudioHandle handle() { 940abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mHandle; 950abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 960abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 970abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 984bcdba848449b33d7022de527c526943aff1f5fdEric Laurent * Get the system unique device ID. 994bcdba848449b33d7022de527c526943aff1f5fdEric Laurent */ 1004bcdba848449b33d7022de527c526943aff1f5fdEric Laurent public int id() { 1014bcdba848449b33d7022de527c526943aff1f5fdEric Laurent return mHandle.id(); 1024bcdba848449b33d7022de527c526943aff1f5fdEric Laurent } 1034bcdba848449b33d7022de527c526943aff1f5fdEric Laurent 1044bcdba848449b33d7022de527c526943aff1f5fdEric Laurent 1054bcdba848449b33d7022de527c526943aff1f5fdEric Laurent /** 1060abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Get the audio port role 1070abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 1080abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public int role() { 1090abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mRole; 1100abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 1110abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 1120abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 11310804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean * Get the human-readable name of this port. Perhaps an internal 11410804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean * designation or an physical device. 11510804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean */ 11610804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean public String name() { 11710804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean return mName; 11810804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean } 11910804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean 12010804eb2818ab59b763a37b4f6151693c2ebba7bPaul McLean /** 1210abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Get the list of supported sampling rates 1220abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Empty array if sampling rate is not relevant for this audio port 1230abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 1240abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public int[] samplingRates() { 1250abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mSamplingRates; 1260abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 1270abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 1280abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 1290abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Get the list of supported channel mask configurations 1300abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * (e.g AudioFormat.CHANNEL_OUT_STEREO) 1310abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Empty array if channel mask is not relevant for this audio port 1320abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 1330abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public int[] channelMasks() { 1340abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mChannelMasks; 1350abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 1360abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 1370abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 138f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean * Get the list of supported channel index mask configurations 139f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean * (e.g 0x0003 means 2 channel, 0x000F means 4 channel....) 140f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean * Empty array if channel index mask is not relevant for this audio port 141f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean */ 142f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean public int[] channelIndexMasks() { 143f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean return mChannelIndexMasks; 144f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean } 145f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean 146f29e5f34b39a5688925ca4654be0eab11277b1ccPaul McLean /** 1470abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Get the list of supported audio format configurations 1480abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * (e.g AudioFormat.ENCODING_PCM_16BIT) 1490abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Empty array if format is not relevant for this audio port 1500abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 1510abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public int[] formats() { 1520abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mFormats; 1530abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 1540abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 1550abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 1560abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Get the list of gain descriptors 1570abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Empty array if this port does not have gain control 1580abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 1590abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public AudioGain[] gains() { 1600abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mGains; 1610abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 1620abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 1630abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 1640abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Get the gain descriptor at a given index 1650abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 1660abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent AudioGain gain(int index) { 167b4e0909fc4fb08f513cb2b39a919b189621be87cEric Laurent if (index < 0 || index >= mGains.length) { 1683a24199a0fe9479802ff6096d82cb3745efa1f1fEric Laurent return null; 1693a24199a0fe9479802ff6096d82cb3745efa1f1fEric Laurent } 1700abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mGains[index]; 1710abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 1720abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 1730abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 1740abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Build a specific configuration of this audio port for use by methods 1750abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * like AudioManager.connectAudioPatch(). 1760abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * @param channelMask The desired channel mask. AudioFormat.CHANNEL_OUT_DEFAULT if no change 1770abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * from active configuration requested. 1780abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * @param format The desired audio format. AudioFormat.ENCODING_DEFAULT if no change 1790abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * from active configuration requested. 1800abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * @param gain The desired gain. null if no gain changed requested. 1810abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 1820abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public AudioPortConfig buildConfig(int samplingRate, int channelMask, int format, 1830abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent AudioGainConfig gain) { 1840abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return new AudioPortConfig(this, samplingRate, channelMask, format, gain); 1850abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 1860abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 1870abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent /** 1880abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent * Get currently active configuration of this audio port. 1890abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent */ 1900abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public AudioPortConfig activeConfig() { 1910abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mActiveConfig; 1920abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 1930abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 1940abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent @Override 1950abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public boolean equals(Object o) { 1960abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent if (o == null || !(o instanceof AudioPort)) { 1970abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return false; 1980abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 1990abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent AudioPort ap = (AudioPort)o; 2000abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mHandle.equals(ap.handle()); 2010abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 2020abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 2030abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent @Override 2040abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent public int hashCode() { 2050abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent return mHandle.hashCode(); 2060abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent } 2070abcc5a0bbf2ad6b70b461cec150b70cfe55d239Eric Laurent 2082754fd0cd3f055b1d5f7f2ea1470b4d84011b379Mike Lockwood @Override 2092754fd0cd3f055b1d5f7f2ea1470b4d84011b379Mike Lockwood public String toString() { 21007cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood String role = Integer.toString(mRole); 21107cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood switch (mRole) { 21207cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood case ROLE_NONE: 21307cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood role = "NONE"; 21407cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood break; 21507cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood case ROLE_SOURCE: 21607cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood role = "SOURCE"; 21707cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood break; 21807cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood case ROLE_SINK: 21907cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood role = "SINK"; 22007cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood break; 22107cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood } 22207cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood return "{mHandle: " + mHandle 22307cd124b09a63985f0b200de248fab165f736ae4Mike Lockwood + ", mRole: " + role 2242754fd0cd3f055b1d5f7f2ea1470b4d84011b379Mike Lockwood + "}"; 2252754fd0cd3f055b1d5f7f2ea1470b4d84011b379Mike Lockwood } 2262754fd0cd3f055b1d5f7f2ea1470b4d84011b379Mike Lockwood} 227