1 /*
2  * Copyright (C) 2015 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 #pragma once
18 
19 #define __STDC_LIMIT_MACROS
20 #include <inttypes.h>
21 
22 #include <sys/types.h>
23 
24 #include <media/AudioContainers.h>
25 #include <utils/Errors.h>
26 #include <utils/Timers.h>
27 #include <utils/KeyedVector.h>
28 #include <system/audio.h>
29 #include "AudioIODescriptorInterface.h"
30 #include "ClientDescriptor.h"
31 #include "DeviceDescriptor.h"
32 #include "PolicyAudioPort.h"
33 #include <vector>
34 
35 namespace android {
36 
37 class IOProfile;
38 class AudioPolicyMix;
39 class AudioPolicyClientInterface;
40 
41 class ActivityTracking
42 {
43 public:
44     virtual ~ActivityTracking() = default;
45     bool isActive(uint32_t inPastMs = 0, nsecs_t sysTime = 0) const
46     {
47         if (mActivityCount > 0) {
48             return true;
49         }
50         if (inPastMs == 0) {
51             return false;
52         }
53         if (sysTime == 0) {
54             sysTime = systemTime();
55         }
56         if (ns2ms(sysTime - mStopTime) < inPastMs) {
57             return true;
58         }
59         return false;
60     }
changeActivityCount(int delta)61     void changeActivityCount(int delta)
62     {
63         if ((delta + (int)mActivityCount) < 0) {
64             LOG_ALWAYS_FATAL("%s: invalid delta %d, refCount %d", __func__, delta, mActivityCount);
65         }
66         mActivityCount += delta;
67         if (!mActivityCount) {
68             setStopTime(systemTime());
69         }
70     }
getActivityCount()71     uint32_t getActivityCount() const { return mActivityCount; }
getStopTime()72     nsecs_t getStopTime() const { return mStopTime; }
setStopTime(nsecs_t stopTime)73     void setStopTime(nsecs_t stopTime) { mStopTime = stopTime; }
74 
dump(String8 * dst,int spaces)75     virtual void dump(String8 *dst, int spaces) const
76     {
77         dst->appendFormat("%*s- ActivityCount: %d, StopTime: %" PRId64 ", ", spaces, "",
78                           getActivityCount(), getStopTime());
79     }
80 private:
81     uint32_t mActivityCount = 0;
82     nsecs_t mStopTime = 0;
83 };
84 
85 /**
86  * @brief VolumeActivity: it tracks the activity for volume policy (volume index, mute,
87  * memorize previous stop, and store mute if incompatible device with another strategy.
88  */
89 class VolumeActivity : public ActivityTracking
90 {
91 public:
isMuted()92     bool isMuted() const { return mMuteCount > 0; }
getMuteCount()93     int getMuteCount() const { return mMuteCount; }
incMuteCount()94     int incMuteCount() { return ++mMuteCount; }
decMuteCount()95     int decMuteCount() { return mMuteCount > 0 ? --mMuteCount : -1; }
96 
dump(String8 * dst,int spaces)97     void dump(String8 *dst, int spaces) const override
98     {
99         ActivityTracking::dump(dst, spaces);
100         dst->appendFormat(", Volume: %.03f, MuteCount: %02d\n", mCurVolumeDb, mMuteCount);
101     }
setVolume(float volumeDb)102     void setVolume(float volumeDb) { mCurVolumeDb = volumeDb; }
getVolume()103     float getVolume() const { return mCurVolumeDb; }
104 
105 private:
106     int mMuteCount = 0; /**< mute request counter */
107     float mCurVolumeDb = NAN; /**< current volume in dB. */
108 };
109 /**
110  * Note: volume activities shall be indexed by CurvesId if we want to allow multiple
111  * curves per volume source, inferring a mute management or volume balancing between HW and SW is
112  * done
113  */
114 using VolumeActivities = std::map<VolumeSource, VolumeActivity>;
115 
116 /**
117  * @brief The Activity class: it tracks the activity for volume policy (volume index, mute,
118  * memorize previous stop, and store mute if incompatible device with another strategy.
119  * Having this class prevents from looping on all attributes (legacy streams) of the strategy
120  */
121 class RoutingActivity : public ActivityTracking
122 {
123 public:
setMutedByDevice(bool isMuted)124     void setMutedByDevice( bool isMuted) { mIsMutedByDevice = isMuted; }
isMutedByDevice()125     bool isMutedByDevice() const { return mIsMutedByDevice; }
126 
dump(String8 * dst,int spaces)127     void dump(String8 *dst, int spaces) const override {
128         ActivityTracking::dump(dst, spaces);
129         dst->appendFormat("\n");
130     }
131 private:
132     /**
133      * strategies muted because of incompatible device selection.
134      * See AudioPolicyManager::checkDeviceMuteStrategies()
135      */
136     bool mIsMutedByDevice = false;
137 };
138 using RoutingActivities = std::map<product_strategy_t, RoutingActivity>;
139 
140 // descriptor for audio outputs. Used to maintain current configuration of each opened audio output
141 // and keep track of the usage of this output by each audio stream type.
142 class AudioOutputDescriptor: public AudioPortConfig,
143         public PolicyAudioPortConfig,
144         public AudioIODescriptorInterface,
145         public ClientMapHandler<TrackClientDescriptor>
146 {
147 public:
148     AudioOutputDescriptor(const sp<PolicyAudioPort>& policyAudioPort,
149                           AudioPolicyClientInterface *clientInterface);
~AudioOutputDescriptor()150     virtual ~AudioOutputDescriptor() {}
151 
152     void dump(String8 *dst) const override;
153     void        log(const char* indent);
154 
devices()155     virtual DeviceVector devices() const { return mDevices; }
156     bool sharesHwModuleWith(const sp<AudioOutputDescriptor>& outputDesc);
supportedDevices()157     virtual DeviceVector supportedDevices() const  { return mDevices; }
isDuplicated()158     virtual bool isDuplicated() const { return false; }
latency()159     virtual uint32_t latency() { return 0; }
160     virtual bool isFixedVolume(const DeviceTypeSet& deviceTypes);
161     virtual bool setVolume(float volumeDb,
162                            VolumeSource volumeSource, const StreamTypeVector &streams,
163                            const DeviceTypeSet& deviceTypes,
164                            uint32_t delayMs,
165                            bool force);
166 
167     /**
168      * @brief setStopTime set the stop time due to the client stoppage or a re routing of this
169      * client
170      * @param client to be considered
171      * @param sysTime when the client stopped/was rerouted
172      */
173     void setStopTime(const sp<TrackClientDescriptor>& client, nsecs_t sysTime);
174 
175     /**
176      * Changes the client->active() state and the output descriptor's global active count,
177      * along with the stream active count and mActiveClients.
178      * The client must be previously added by the base class addClient().
179      * In case of duplicating thread, client shall be added on the duplicated thread, not on the
180      * involved outputs but setClientActive will be called on all output to track strategy and
181      * active client for a given output.
182      * Active ref count of the client will be incremented/decremented through setActive API
183      */
184     virtual void setClientActive(const sp<TrackClientDescriptor>& client, bool active);
185     bool isClientActive(const sp<TrackClientDescriptor>& client);
186 
187     bool isActive(uint32_t inPastMs) const;
188     bool isActive(VolumeSource volumeSource = VOLUME_SOURCE_NONE,
189                   uint32_t inPastMs = 0,
190                   nsecs_t sysTime = 0) const;
191     bool isAnyActive(VolumeSource volumeSourceToIgnore) const;
192 
getActiveVolumeSources()193     std::vector<VolumeSource> getActiveVolumeSources() const {
194         std::vector<VolumeSource> activeList;
195         for (const auto &iter : mVolumeActivities) {
196             if (iter.second.isActive()) {
197                 activeList.push_back(iter.first);
198             }
199         }
200         return activeList;
201     }
getActivityCount(VolumeSource vs)202     uint32_t getActivityCount(VolumeSource vs) const
203     {
204         return mVolumeActivities.find(vs) != std::end(mVolumeActivities)?
205                     mVolumeActivities.at(vs).getActivityCount() : 0;
206     }
isMuted(VolumeSource vs)207     bool isMuted(VolumeSource vs) const
208     {
209         return mVolumeActivities.find(vs) != std::end(mVolumeActivities)?
210                     mVolumeActivities.at(vs).isMuted() : false;
211     }
getMuteCount(VolumeSource vs)212     int getMuteCount(VolumeSource vs) const
213     {
214         return mVolumeActivities.find(vs) != std::end(mVolumeActivities)?
215                     mVolumeActivities.at(vs).getMuteCount() : 0;
216     }
incMuteCount(VolumeSource vs)217     int incMuteCount(VolumeSource vs)
218     {
219         return mVolumeActivities[vs].incMuteCount();
220     }
decMuteCount(VolumeSource vs)221     int decMuteCount(VolumeSource vs)
222     {
223         return mVolumeActivities[vs].decMuteCount();
224     }
setCurVolume(VolumeSource vs,float volumeDb)225     void setCurVolume(VolumeSource vs, float volumeDb)
226     {
227         // Even if not activity for this source registered, need to create anyway
228         mVolumeActivities[vs].setVolume(volumeDb);
229     }
getCurVolume(VolumeSource vs)230     float getCurVolume(VolumeSource vs) const
231     {
232         return mVolumeActivities.find(vs) != std::end(mVolumeActivities) ?
233                     mVolumeActivities.at(vs).getVolume() : NAN;
234     }
235 
236     bool isStrategyActive(product_strategy_t ps, uint32_t inPastMs = 0, nsecs_t sysTime = 0) const
237     {
238         return mRoutingActivities.find(ps) != std::end(mRoutingActivities)?
239                     mRoutingActivities.at(ps).isActive(inPastMs, sysTime) : false;
240     }
isStrategyMutedByDevice(product_strategy_t ps)241     bool isStrategyMutedByDevice(product_strategy_t ps) const
242     {
243         return mRoutingActivities.find(ps) != std::end(mRoutingActivities)?
244                     mRoutingActivities.at(ps).isMutedByDevice() : false;
245     }
setStrategyMutedByDevice(product_strategy_t ps,bool isMuted)246     void setStrategyMutedByDevice(product_strategy_t ps, bool isMuted)
247     {
248         mRoutingActivities[ps].setMutedByDevice(isMuted);
249     }
250 
251     // PolicyAudioPortConfig
getPolicyAudioPort()252     virtual sp<PolicyAudioPort> getPolicyAudioPort() const
253     {
254         return mPolicyAudioPort;
255     }
256 
257     // AudioPortConfig
258     virtual status_t applyAudioPortConfig(const struct audio_port_config *config,
259                                           struct audio_port_config *backupConfig = NULL);
260     virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
261                            const struct audio_port_config *srcConfig = NULL) const;
getAudioPort()262     virtual sp<AudioPort> getAudioPort() const { return mPolicyAudioPort->asAudioPort(); }
263 
264     virtual void toAudioPort(struct audio_port_v7 *port) const;
265 
266     audio_module_handle_t getModuleHandle() const;
267 
268     // implementation of AudioIODescriptorInterface
269     audio_config_base_t getConfig() const override;
270     audio_patch_handle_t getPatchHandle() const override;
271     void setPatchHandle(audio_patch_handle_t handle) override;
isMmap()272     bool isMmap() override {
273         if (getPolicyAudioPort() != nullptr) {
274             return getPolicyAudioPort()->isMmap();
275         }
276         return false;
277     }
278 
279     TrackClientVector clientsList(bool activeOnly = false,
280                                   product_strategy_t strategy = PRODUCT_STRATEGY_NONE,
281                                   bool preferredDeviceOnly = false) const;
282 
283     // override ClientMapHandler to abort when removing a client when active.
removeClient(audio_port_handle_t portId)284     void removeClient(audio_port_handle_t portId) override {
285         auto client = getClient(portId);
286         LOG_ALWAYS_FATAL_IF(client.get() == nullptr,
287                 "%s(%d): nonexistent client portId %d", __func__, mId, portId);
288         // it is possible that when a client is removed, we could remove its
289         // associated active count by calling changeStreamActiveCount(),
290         // but that would be hiding a problem, so we log fatal instead.
291         auto clientIter = std::find(begin(mActiveClients), end(mActiveClients), client);
292         LOG_ALWAYS_FATAL_IF(clientIter != mActiveClients.end(),
293                             "%s(%d) removing client portId %d which is active (count %d)",
294                             __func__, mId, portId, client->getActivityCount());
295         ClientMapHandler<TrackClientDescriptor>::removeClient(portId);
296     }
297 
getActiveClients()298     const TrackClientVector& getActiveClients() const {
299         return mActiveClients;
300     }
301 
useHwGain()302     bool useHwGain() const
303     {
304         return !devices().isEmpty() ? devices().itemAt(0)->hasGainController() : false;
305     }
306 
307     DeviceVector mDevices; /**< current devices this output is routed to */
308     wp<AudioPolicyMix> mPolicyMix;  // non NULL when used by a dynamic policy
309 
310 protected:
311     const sp<PolicyAudioPort> mPolicyAudioPort;
312     AudioPolicyClientInterface * const mClientInterface;
313     uint32_t mGlobalActiveCount = 0;  // non-client-specific active count
314     audio_patch_handle_t mPatchHandle = AUDIO_PATCH_HANDLE_NONE;
315 
316     // The ActiveClients shows the clients that contribute to the @VolumeSource counts
317     // and may include upstream clients from a duplicating thread.
318     // Compare with the ClientMap (mClients) which are external AudioTrack clients of the
319     // output descriptor (and do not count internal PatchTracks).
320     TrackClientVector mActiveClients;
321 
322     RoutingActivities mRoutingActivities; /**< track routing activity on this ouput.*/
323 
324     VolumeActivities mVolumeActivities; /**< track volume activity on this ouput.*/
325 };
326 
327 // Audio output driven by a software mixer in audio flinger.
328 class SwAudioOutputDescriptor: public AudioOutputDescriptor
329 {
330 public:
331     SwAudioOutputDescriptor(const sp<IOProfile>& profile,
332                             AudioPolicyClientInterface *clientInterface);
~SwAudioOutputDescriptor()333     virtual ~SwAudioOutputDescriptor() {}
334 
335             void dump(String8 *dst) const override;
336     virtual DeviceVector devices() const;
setDevices(const DeviceVector & devices)337     void setDevices(const DeviceVector &devices) { mDevices = devices; }
338     bool sharesHwModuleWith(const sp<SwAudioOutputDescriptor>& outputDesc);
339     virtual DeviceVector supportedDevices() const;
340     virtual bool devicesSupportEncodedFormats(const DeviceTypeSet& deviceTypes);
341     virtual bool containsSingleDeviceSupportingEncodedFormats(
342             const sp<DeviceDescriptor>& device) const;
343     virtual uint32_t latency();
isDuplicated()344     virtual bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); }
345     virtual bool isFixedVolume(const DeviceTypeSet& deviceTypes);
subOutput1()346     sp<SwAudioOutputDescriptor> subOutput1() { return mOutput1; }
subOutput2()347     sp<SwAudioOutputDescriptor> subOutput2() { return mOutput2; }
348     void setClientActive(const sp<TrackClientDescriptor>& client, bool active) override;
setAllClientsInactive()349     void setAllClientsInactive()
350     {
351         for (const auto &client : clientsList(true)) {
352             setClientActive(client, false);
353         }
354     }
355     virtual bool setVolume(float volumeDb,
356                            VolumeSource volumeSource, const StreamTypeVector &streams,
357                            const DeviceTypeSet& device,
358                            uint32_t delayMs,
359                            bool force);
360 
361     virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
362                            const struct audio_port_config *srcConfig = NULL) const;
363     virtual void toAudioPort(struct audio_port_v7 *port) const;
364 
365         status_t open(const audio_config_t *halConfig,
366                       const audio_config_base_t *mixerConfig,
367                       const DeviceVector &devices,
368                       audio_stream_type_t stream,
369                       audio_output_flags_t flags,
370                       audio_io_handle_t *output);
371 
372         // Called when a stream is about to be started
373         // Note: called before setClientActive(true);
374         status_t start();
375         // Called after a stream is stopped.
376         // Note: called after setClientActive(false);
377         void stop();
378         void close();
379         status_t openDuplicating(const sp<SwAudioOutputDescriptor>& output1,
380                                  const sp<SwAudioOutputDescriptor>& output2,
381                                  audio_io_handle_t *ioHandle);
382 
383     /**
384      * @brief supportsDevice
385      * @param device to be checked against
386      * @return true if the device is supported by type (for non bus / remote submix devices),
387      *         true if the device is supported (both type and address) for bus / remote submix
388      *         false otherwise
389      */
390     bool supportsDevice(const sp<DeviceDescriptor> &device) const;
391 
392     /**
393      * @brief supportsAllDevices
394      * @param devices to be checked against
395      * @return true if the device is weakly supported by type (e.g. for non bus / rsubmix devices),
396      *         true if the device is supported (both type and address) for bus / remote submix
397      *         false otherwise
398      */
399     bool supportsAllDevices(const DeviceVector &devices) const;
400 
401     /**
402      * @brief supportsDevicesForPlayback
403      * @param devices to be checked against
404      * @return true if the devices is a supported combo for playback
405      *         false otherwise
406      */
407     bool supportsDevicesForPlayback(const DeviceVector &devices) const;
408 
409     /**
410      * @brief filterSupportedDevices takes a vector of devices and filters them according to the
411      * device supported by this output (the profile from which this output derives from)
412      * @param devices reference device vector to be filtered
413      * @return vector of devices filtered from the supported devices of this output (weakly or not
414      * depending on the device type)
415      */
416     DeviceVector filterSupportedDevices(const DeviceVector &devices) const;
417 
418     const sp<IOProfile> mProfile;          // I/O profile this output derives from
419     audio_io_handle_t mIoHandle;           // output handle
420     uint32_t mLatency;                  //
421     audio_output_flags_t mFlags;   //
422     sp<SwAudioOutputDescriptor> mOutput1;    // used by duplicated outputs: first output
423     sp<SwAudioOutputDescriptor> mOutput2;    // used by duplicated outputs: second output
424     uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
425     audio_session_t mDirectClientSession; // session id of the direct output client
426     bool mPendingReopenToQueryProfiles = false;
427     audio_channel_mask_t mMixerChannelMask = AUDIO_CHANNEL_NONE;
428 };
429 
430 // Audio output driven by an input device directly.
431 class HwAudioOutputDescriptor: public AudioOutputDescriptor
432 {
433 public:
434     HwAudioOutputDescriptor(const sp<SourceClientDescriptor>& source,
435                             AudioPolicyClientInterface *clientInterface);
~HwAudioOutputDescriptor()436     virtual ~HwAudioOutputDescriptor() {}
437 
438             void dump(String8 *dst) const override;
439 
440     virtual bool setVolume(float volumeDb,
441                            VolumeSource volumeSource, const StreamTypeVector &streams,
442                            const DeviceTypeSet& deviceTypes,
443                            uint32_t delayMs,
444                            bool force);
445 
446     virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
447                            const struct audio_port_config *srcConfig = NULL) const;
448     virtual void toAudioPort(struct audio_port_v7 *port) const;
449 
450     const sp<SourceClientDescriptor> mSource;
451 
452 };
453 
454 class SwAudioOutputCollection :
455         public DefaultKeyedVector< audio_io_handle_t, sp<SwAudioOutputDescriptor> >
456 {
457 public:
458     bool isActive(VolumeSource volumeSource, uint32_t inPastMs = 0) const;
459 
460     /**
461      * return whether any source contributing to VolumeSource is playing remotely, override
462      * to change the definition of
463      * local/remote playback, used for instance by notification manager to not make
464      * media players lose audio focus when not playing locally
465      * For the base implementation, "remotely" means playing during screen mirroring which
466      * uses an output for playback with a non-empty, non "0" address.
467      */
468     bool isActiveRemotely(VolumeSource volumeSource, uint32_t inPastMs = 0) const;
469 
470     /**
471      * return whether any source contributing to VolumeSource is playing, but not on a "remote"
472      * device.
473      * Override to change the definition of a local/remote playback.
474      * Used for instance by policy manager to alter the speaker playback ("speaker safe" behavior)
475      * when media plays or not locally.
476      * For the base implementation, "remotely" means playing during screen mirroring.
477      */
478     bool isActiveLocally(VolumeSource volumeSource, uint32_t inPastMs = 0) const;
479 
480     /**
481      * @brief isStrategyActiveOnSameModule checks if the given strategy is active (or was active
482      * in the past) on the given output and all the outputs belonging to the same HW Module
483      * the same module than the given output
484      * @param outputDesc to be considered
485      * @param ps product strategy to be checked upon activity status
486      * @param inPastMs if 0, check currently, otherwise, check in the past
487      * @param sysTime shall be set if request is done for the past activity.
488      * @return true if an output following the strategy is active on the same module than desc,
489      * false otherwise
490      */
491     bool isStrategyActiveOnSameModule(product_strategy_t ps,
492                                       const sp<SwAudioOutputDescriptor>& desc,
493                                       uint32_t inPastMs = 0, nsecs_t sysTime = 0) const;
494 
495     /**
496      * @brief clearSessionRoutesForDevice: when a device is disconnected, and if this device has
497      * been chosen as the preferred device by any client, the policy manager shall
498      * prevent from using this device any more by clearing all the session routes involving this
499      * device.
500      * In other words, the preferred device port id of these clients will be resetted to NONE.
501      * @param disconnectedDevice device to be disconnected
502      */
503     void clearSessionRoutesForDevice(const sp<DeviceDescriptor> &disconnectedDevice);
504 
505     /**
506      * returns the A2DP output handle if it is open or 0 otherwise
507      */
508     audio_io_handle_t getA2dpOutput() const;
509 
510     /**
511      * returns true if primary HAL supports A2DP Offload
512      */
513     bool isA2dpOffloadedOnPrimary() const;
514 
515     sp<SwAudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const;
516 
517     sp<SwAudioOutputDescriptor> getPrimaryOutput() const;
518 
519     /**
520      * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that
521      * hold the volume source to be ignored
522      * @param volumeSourceToIgnore source not to be considered in the activity detection
523      * @return true if any output is active for any volume source except the one to be ignored
524      */
isAnyOutputActive(VolumeSource volumeSourceToIgnore)525     bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const
526     {
527         for (size_t i = 0; i < size(); i++) {
528             const sp<AudioOutputDescriptor> &outputDesc = valueAt(i);
529             if (outputDesc->isAnyActive(volumeSourceToIgnore)) {
530                 return true;
531             }
532         }
533         return false;
534     }
535 
536     audio_devices_t getSupportedDevices(audio_io_handle_t handle) const;
537 
538     sp<SwAudioOutputDescriptor> getOutputForClient(audio_port_handle_t portId);
539 
540     void dump(String8 *dst) const;
541 };
542 
543 class HwAudioOutputCollection :
544         public DefaultKeyedVector< audio_io_handle_t, sp<HwAudioOutputDescriptor> >
545 {
546 public:
547     bool isActive(VolumeSource volumeSource, uint32_t inPastMs = 0) const;
548 
549     /**
550      * @brief isAnyOutputActive checks if any output is active (aka playing) except the one(s) that
551      * hold the volume source to be ignored
552      * @param volumeSourceToIgnore source not to be considered in the activity detection
553      * @return true if any output is active for any volume source except the one to be ignored
554      */
isAnyOutputActive(VolumeSource volumeSourceToIgnore)555     bool isAnyOutputActive(VolumeSource volumeSourceToIgnore) const
556     {
557         for (size_t i = 0; i < size(); i++) {
558             const sp<AudioOutputDescriptor> &outputDesc = valueAt(i);
559             if (outputDesc->isAnyActive(volumeSourceToIgnore)) {
560                 return true;
561             }
562         }
563         return false;
564     }
565 
566     void dump(String8 *dst) const;
567 };
568 
569 
570 } // namespace android
571