Search code examples
iosnotificationstheostweak

AVAudioSession instance be deactived just after handling AVAudioSessionInterruptionNotification ended signal


I am trying to write a tweak in Theos for an iOS app, to play a silent wav in background for more background life. There might be incoming calls interrupt the sound playing, so I wrote the following notification handling. It should work, the audio player can be restarted, but playing the sound for a very short time, about 0.3s. And then the player will be deactived and stopped.

I've already tried to insert many many hook logs, still not find any clue. Anyone help me? I am very appreciated. I am new to developing iOS tweaks. Are there some tools to debug this issue?

// Register interruption handler
[[NSNotificationCenter defaultCenter]  addObserver:self  
                                          selector:@selector(audioSessionInterruption:)  
                                          name:AVAudioSessionInterruptionNotification  
                                          object:nil];

- (void)audioSessionInterruption:(NSNotification *)notification{
   NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];
   NSNumber *interruptionOption = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];

   switch (interruptionType.unsignedIntegerValue) {
      case AVAudioSessionInterruptionTypeBegan:{
         NSLog(@"AVAudioSessionInterruptionTypeBegan");
      } break;
      case AVAudioSessionInterruptionTypeEnded:{
         if (interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume) {
               // Here should continue playback. Active audio session and play
               // Unfortunately, the audio player playing less one second, and be deactived by something
               NSLog(@"AVAudioSessionInterruptionTypeEnded");
               [[AVAudioSession sharedInstance] setActive:YES error:nil];
               [self.audioPlayer play];
         }
      } break;
      default:
         break;
   }
}

Check the logs, I found that the AudioSession was successfully activated but was promptly deactivated.

CAReportingClient.mm:508   message {API = "[AVAudioSession etActive:activate]";    ElapsedTime = "6.517041";}: (176093659500)
CAReportingClient.mm:508   message {API = "-[AVAudioSession ategory]";    ElapsedTime = "0.158375";}: (176093659500)
CAReportingClient.mm:508   message {API = "-[AVAudioSession privateSetCategoryWithOptions:modes:routeSharingPolicy:options:]";    ElapsedTime = "5.618958";}: (176093659500)
AVAudioSession_iOS.mm:1271  Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session.
CAReportingClient.mm:508   message {API = "[AVAudioSession setActive:deactivate]";    ElapsedTime = "656.548541";}: (176093659500)

I want to find out which process deactive the audio player, and avoid it or write a hook to handle it. May be there is another notification interruption handler in this IOS app?


Solution

  • Finally I found the solution. Something changed audio session category options just after interruption being ended. The workaround is make a delay 3 seconds and change it back

    - (void)audioSessionInterruption:(NSNotification *)notification{
       NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];
       NSNumber *interruptionOption = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];
    
       switch (interruptionType.unsignedIntegerValue) {
          case AVAudioSessionInterruptionTypeBegan:{
             // • Audio has stopped, already inactive
             // • Change state of UI, etc., to reflect non-playing state
             NSLog(@"AVAudioSession interruption type began");
          } break;
          case AVAudioSessionInterruptionTypeEnded:{
             // • Make session active
             // • Update user interface
             // • AVAudioSessionInterruptionOptionShouldResume option
             if (interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume) {
                   // Here you should continue playback.
                   NSLog(@"AVAudioSession interruption type ended");
                   // Something changed audio session category options, here delay 3 seconds and change it back
                   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                      NSLog(@"Resume silence audio play from interruption");
                      [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback 
                                                       withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
                      [[AVAudioSession sharedInstance] setActive:YES error:nil];
                      [self.audioPlayer play];
                   });
             }
          } break;
          default:
             break;
       }
    }