Support delayed AudioUnit initialization.

Applications can choose to decide when to give up control of the
AVAudioSession to WebRTC. Otherwise, behavior should be
unchanged.

Adds a toggle to AppRTCDemo so developers can see the different
paths.

BUG=
R=haysc@webrtc.org

Review URL: https://codereview.webrtc.org/1822543002 .

Cr-Commit-Position: refs/heads/master@{#12080}
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDAppDelegate.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDAppDelegate.m
index 7dda2fc..b169ca4 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDAppDelegate.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDAppDelegate.m
@@ -43,12 +43,6 @@
   return YES;
 }
 
-- (void)applicationWillResignActive:(UIApplication *)application {
-  ARDMainViewController *viewController =
-      (ARDMainViewController *)_window.rootViewController;
-  [viewController applicationWillResignActive:application];
-}
-
 - (void)applicationWillTerminate:(UIApplication *)application {
   RTCShutdownInternalTracer();
   RTCCleanupSSL();
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
index a77cc8c..d3a583d 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
@@ -15,9 +15,12 @@
 @protocol ARDMainViewDelegate <NSObject>
 
 - (void)mainView:(ARDMainView *)mainView
-    didInputRoom:(NSString *)room
-      isLoopback:(BOOL)isLoopback
-     isAudioOnly:(BOOL)isAudioOnly;
+             didInputRoom:(NSString *)room
+               isLoopback:(BOOL)isLoopback
+              isAudioOnly:(BOOL)isAudioOnly
+   shouldDelayAudioConfig:(BOOL)shouldDelayAudioConfig;
+
+- (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView;
 
 @end
 
@@ -26,5 +29,7 @@
 @interface ARDMainView : UIView
 
 @property(nonatomic, weak) id<ARDMainViewDelegate> delegate;
+// Updates the audio loop button as needed.
+@property(nonatomic, assign) BOOL isAudioLoopPlaying;
 
 @end
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
index 6f52657..b264763 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
@@ -10,8 +10,6 @@
 
 #import "ARDMainView.h"
 
-#import <AVFoundation/AVFoundation.h>
-
 #import "UIImage+ARDUtilities.h"
 
 // TODO(tkchin): retrieve status bar height dynamically.
@@ -124,24 +122,17 @@
   UILabel *_audioOnlyLabel;
   UISwitch *_loopbackSwitch;
   UILabel *_loopbackLabel;
+  UISwitch *_audioConfigDelaySwitch;
+  UILabel *_audioConfigDelayLabel;
   UIButton *_startCallButton;
   UIButton *_audioLoopButton;
-  AVAudioPlayer *_audioPlayer;
 }
 
 @synthesize delegate = _delegate;
+@synthesize isAudioLoopPlaying = _isAudioLoopPlaying;
 
 - (instancetype)initWithFrame:(CGRect)frame {
   if (self = [super initWithFrame:frame]) {
-    NSString *audioFilePath =
-        [[NSBundle mainBundle] pathForResource:@"mozart" ofType:@"mp3"];
-    NSURL *audioFileURL = [NSURL URLWithString:audioFilePath];
-    _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL
-                                                          error:nil];
-    _audioPlayer.numberOfLoops = -1;
-    _audioPlayer.volume = 1.0;
-    [_audioPlayer prepareToPlay];
-
     _appLabel = [[UILabel alloc] initWithFrame:CGRectZero];
     _appLabel.text = @"AppRTCDemo";
     _appLabel.font = [UIFont fontWithName:@"Roboto" size:34];
@@ -184,6 +175,18 @@
     [_loopbackLabel sizeToFit];
     [self addSubview:_loopbackLabel];
 
+    _audioConfigDelaySwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
+    [_audioConfigDelaySwitch sizeToFit];
+    _audioConfigDelaySwitch.on = YES;
+    [self addSubview:_audioConfigDelaySwitch];
+
+    _audioConfigDelayLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+    _audioConfigDelayLabel.text = @"Delay audio config";
+    _audioConfigDelayLabel.font = controlFont;
+    _audioConfigDelayLabel.textColor = controlFontColor;
+    [_audioConfigDelayLabel sizeToFit];
+    [self addSubview:_audioConfigDelayLabel];
+
     _startCallButton = [UIButton buttonWithType:UIButtonTypeSystem];
     _startCallButton.backgroundColor = [UIColor blueColor];
     _startCallButton.layer.cornerRadius = 10;
@@ -223,6 +226,14 @@
   return self;
 }
 
+- (void)setIsAudioLoopPlaying:(BOOL)isAudioLoopPlaying {
+  if (_isAudioLoopPlaying == isAudioLoopPlaying) {
+    return;
+  }
+  _isAudioLoopPlaying = isAudioLoopPlaying;
+  [self updateAudioLoopButton];
+}
+
 - (void)layoutSubviews {
   CGRect bounds = self.bounds;
   CGFloat roomTextWidth = bounds.size.width - 2 * kRoomTextFieldMargin;
@@ -264,8 +275,22 @@
   _loopbackLabel.center = CGPointMake(loopbackModeLabelCenterX,
                                       CGRectGetMidY(loopbackModeRect));
 
+  CGFloat audioConfigDelayTop =
+      CGRectGetMaxY(_loopbackSwitch.frame) + kCallControlMargin;
+  CGRect audioConfigDelayRect =
+      CGRectMake(kCallControlMargin * 3,
+                 audioConfigDelayTop,
+                 _audioConfigDelaySwitch.frame.size.width,
+                 _audioConfigDelaySwitch.frame.size.height);
+  _audioConfigDelaySwitch.frame = audioConfigDelayRect;
+  CGFloat audioConfigDelayLabelCenterX = CGRectGetMaxX(audioConfigDelayRect) +
+      kCallControlMargin + _audioConfigDelayLabel.frame.size.width / 2;
+  _audioConfigDelayLabel.center =
+      CGPointMake(audioConfigDelayLabelCenterX,
+                  CGRectGetMidY(audioConfigDelayRect));
+
   CGFloat audioLoopTop =
-     CGRectGetMaxY(loopbackModeRect) + kCallControlMargin * 3;
+     CGRectGetMaxY(audioConfigDelayRect) + kCallControlMargin * 3;
   _audioLoopButton.frame = CGRectMake(kCallControlMargin,
                                       audioLoopTop,
                                       _audioLoopButton.frame.size.width,
@@ -282,7 +307,7 @@
 #pragma mark - Private
 
 - (void)updateAudioLoopButton {
-  if (_audioPlayer.playing) {
+  if (_isAudioLoopPlaying) {
     _audioLoopButton.backgroundColor = [UIColor redColor];
     [_audioLoopButton setTitle:@"Stop sound"
                       forState:UIControlStateNormal];
@@ -296,12 +321,7 @@
 }
 
 - (void)onToggleAudioLoop:(id)sender {
-  if (_audioPlayer.playing) {
-    [_audioPlayer stop];
-  } else {
-    [_audioPlayer play];
-  }
-  [self updateAudioLoopButton];
+  [_delegate mainViewDidToggleAudioLoop:self];
 }
 
 - (void)onStartCall:(id)sender {
@@ -312,9 +332,10 @@
   }
   room = [room stringByReplacingOccurrencesOfString:@"-" withString:@""];
   [_delegate mainView:self
-         didInputRoom:room
-           isLoopback:_loopbackSwitch.isOn
-          isAudioOnly:_audioOnlySwitch.isOn];
+                didInputRoom:room
+                  isLoopback:_loopbackSwitch.isOn
+                 isAudioOnly:_audioOnlySwitch.isOn
+      shouldDelayAudioConfig:_audioConfigDelaySwitch.isOn];
 }
 
 @end
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.h b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.h
index cc38170..e5c92dd 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.h
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.h
@@ -11,7 +11,4 @@
 #import <UIKit/UIKit.h>
 
 @interface ARDMainViewController : UIViewController
-
-- (void)applicationWillResignActive:(UIApplication *)application;
-
 @end
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
index 8de6f6a1..a634952 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
@@ -10,32 +10,44 @@
 
 #import "ARDMainViewController.h"
 
+#import <AVFoundation/AVFoundation.h>
+
+#import "webrtc/base/objc/RTCDispatcher.h"
+#import "webrtc/base/objc/RTCLogging.h"
+#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h"
+#import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionConfiguration.h"
+
 #import "ARDAppClient.h"
 #import "ARDMainView.h"
 #import "ARDVideoCallViewController.h"
 
-@interface ARDMainViewController () <ARDMainViewDelegate>
+@interface ARDMainViewController () <
+    ARDMainViewDelegate,
+    RTCAudioSessionDelegate>
 @end
 
-@implementation ARDMainViewController
-
-- (void)loadView {
-  ARDMainView *mainView = [[ARDMainView alloc] initWithFrame:CGRectZero];
-  mainView.delegate = self;
-  self.view = mainView;
+@implementation ARDMainViewController {
+  ARDMainView *_mainView;
+  AVAudioPlayer *_audioPlayer;
+  BOOL _shouldDelayAudioConfig;
 }
 
-- (void)applicationWillResignActive:(UIApplication *)application {
-  // Terminate any calls when we aren't active.
-  [self dismissViewControllerAnimated:NO completion:nil];
+- (void)loadView {
+  _mainView = [[ARDMainView alloc] initWithFrame:CGRectZero];
+  _mainView.delegate = self;
+  self.view = _mainView;
+
+  [self setupAudioSession];
+  [self setupAudioPlayer];
 }
 
 #pragma mark - ARDMainViewDelegate
 
 - (void)mainView:(ARDMainView *)mainView
-    didInputRoom:(NSString *)room
-      isLoopback:(BOOL)isLoopback
-     isAudioOnly:(BOOL)isAudioOnly {
+              didInputRoom:(NSString *)room
+                isLoopback:(BOOL)isLoopback
+               isAudioOnly:(BOOL)isAudioOnly
+    shouldDelayAudioConfig:(BOOL)shouldDelayAudioConfig {
   if (!room.length) {
     [self showAlertWithMessage:@"Missing room name."];
     return;
@@ -65,6 +77,10 @@
     return;
   }
 
+  _shouldDelayAudioConfig = shouldDelayAudioConfig;
+  RTCAudioSession *session = [RTCAudioSession sharedInstance];
+  session.shouldDelayAudioConfiguration = _shouldDelayAudioConfig;
+
   // Kick off the video call.
   ARDVideoCallViewController *videoCallViewController =
       [[ARDVideoCallViewController alloc] initForRoom:trimmedRoom
@@ -77,8 +93,82 @@
                    completion:nil];
 }
 
+- (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView {
+  if (mainView.isAudioLoopPlaying) {
+    [_audioPlayer stop];
+  } else {
+    [_audioPlayer play];
+  }
+  mainView.isAudioLoopPlaying = _audioPlayer.playing;
+}
+
+#pragma mark - RTCAudioSessionDelegate
+
+- (void)audioSessionShouldConfigure:(RTCAudioSession *)session {
+  // Won't get called unless audio config is delayed.
+  // Stop playback on main queue and then configure WebRTC.
+  [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeMain
+                               block:^{
+    if (_mainView.isAudioLoopPlaying) {
+      RTCLog(@"Stopping audio loop due to WebRTC start.");
+      [_audioPlayer stop];
+    }
+    // TODO(tkchin): Shouldn't lock on main queue. Figure out better way to
+    // check audio loop state.
+    [session lockForConfiguration];
+    [session configureWebRTCSession:nil];
+    [session unlockForConfiguration];
+  }];
+}
+
+- (void)audioSessionShouldUnconfigure:(RTCAudioSession *)session {
+  // Won't get called unless audio config is delayed.
+  [session lockForConfiguration];
+  [session unconfigureWebRTCSession:nil];
+  [session unlockForConfiguration];
+}
+
+- (void)audioSessionDidUnconfigure:(RTCAudioSession *)session {
+  // WebRTC is done with the audio session. Restart playback.
+  [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeMain
+                               block:^{
+    if (_mainView.isAudioLoopPlaying) {
+      RTCLog(@"Starting audio loop due to WebRTC end.");
+      [_audioPlayer play];
+    }
+  }];
+}
+
 #pragma mark - Private
 
+- (void)setupAudioSession {
+  RTCAudioSessionConfiguration *configuration =
+      [[RTCAudioSessionConfiguration alloc] init];
+  configuration.category = AVAudioSessionCategoryAmbient;
+  configuration.categoryOptions = AVAudioSessionCategoryOptionDuckOthers;
+  configuration.mode = AVAudioSessionModeDefault;
+
+  RTCAudioSession *session = [RTCAudioSession sharedInstance];
+  [session addDelegate:self];
+  [session lockForConfiguration];
+  NSError *error = nil;
+  if (![session setConfiguration:configuration active:YES error:&error]) {
+    RTCLogError(@"Error setting configuration: %@", error.localizedDescription);
+  }
+  [session unlockForConfiguration];
+}
+
+- (void)setupAudioPlayer {
+  NSString *audioFilePath =
+      [[NSBundle mainBundle] pathForResource:@"mozart" ofType:@"mp3"];
+  NSURL *audioFileURL = [NSURL URLWithString:audioFilePath];
+  _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL
+                                                        error:nil];
+  _audioPlayer.numberOfLoops = -1;
+  _audioPlayer.volume = 1.0;
+  [_audioPlayer prepareToPlay];
+}
+
 - (void)showAlertWithMessage:(NSString*)message {
   UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:nil
                                                       message:message