Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to start service com.twiliovoicereactnative.VoiceService #430

Open
3 tasks done
mudassir-iqbal opened this issue Sep 26, 2024 · 5 comments
Open
3 tasks done
Assignees

Comments

@mudassir-iqbal
Copy link

mudassir-iqbal commented Sep 26, 2024

Issue

In Android users occasionally experience crashes or are unable to make calls. The issue does not affect all users and seems to occur intermittently. W've tried to reproduce this locally but not luck. Mostly users are Samsung user who reported this issue.

Pre-submission Checklist

  • I have verified that the issue occurs with the latest release and is not marked as a known issue in the CHANGELOG.md.
  • I reviewed the Common Issues and open GitHub issues and verified that this report represents a potentially new issue.
  • I am not sharing any Personally Identifiable Information (PII)
    or sensitive account information (API keys, credentials, etc.) when reporting this issue.

Description

- Platform: android
Fatal Exception: java.lang.RuntimeException: Unable to start service com.twiliovoicereactnative.VoiceService@158663a with null: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Intent.getAction()' on a null object reference
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5286)
       at android.app.ActivityThread.-$$Nest$mhandleServiceArgs()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2531)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:230)
       at android.os.Looper.loop(Looper.java:319)
       at android.app.ActivityThread.main(ActivityThread.java:8919)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)

Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Intent.getAction()' on a null object reference
       at com.twiliovoicereactnative.VoiceService.onStartCommand(VoiceService.java)
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5268)
       at android.app.ActivityThread.-$$Nest$mhandleServiceArgs()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2531)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:230)
       at android.os.Looper.loop(Looper.java:319)
       at android.app.ActivityThread.main(ActivityThread.java:8919)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)

queued-work-looper-data:
       at jdk.internal.misc.Unsafe.park(Unsafe.java)
       at java.util.concurrent.locks.LockSupport.park(LockSupport.java:341)
       at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block(AbstractQueuedSynchronizer.java:506)
       at java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3466)
       at java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3437)
       at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1623)
       at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:435)
       at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1071)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1131)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
       at java.lang.Thread.run(Thread.java:1012)

Reproduction Steps

As I mention we are unable to reproduce this locally, this happen only in release builds and reported by users.

Expected Behavior

The application should allow users to initiate calls without crashing.

Actual Behavior

Some users encounter crashes or are unable to make calls. The crash logs typically include a NullPointerException related to a null Intent in the VoiceService.

Reproduction Frequency

This issue has 19 crash events affecting 5 users in last 7 days. All Samsung devices.

Software and Device Information

  • Device: Samsung (Galaxy A53 5G, Galaxy A34 5G, Galaxy S21 Ultra 5G, Galaxy A55 5G)
  • OS: Android 14
  • React version: 18.2.0
  • React Native version: 0.73.8
  • Node version: 20.10.0
  • npm or yarn version: 10.2.3, 1.22.22

Additional Context

Here is my callService.ts

class CallService implements ICallService {
  private voice: Voice | null = null;
  private callStatus: ECallStatus = ECallStatus.Idle;
  private statusSubscribers: Array<(status: ECallStatus) => void> = [];
  private activeCall: Call | null = null;
  private token: string | null = null;

  public async initialize(identity: string): Promise<void> {
    try {
      if (!this.token) {
        this.token = await getAccessToken(identity);
      }
      this.voice = new Voice();
      if (Platform.OS === 'ios') {
        await this.voice.initializePushRegistry();
      }
      await this.voice.register(this.token);
      console.log('Voice SDK initialized and registered');
    } catch (error) {
      console.log('Error initializing Voice SDK:', error);
    }
  }

  public async makeCall({to, from, identity}: IMakeCallParams): Promise<void> {
    try {
      if (!this.voice) {
        console.log('Voice SDK is not initialized');
        return;
      }
      this.setStatus(ECallStatus.Connecting);
      if (!this.token) {
        this.token = await getAccessToken(identity);
      }
      this.activeCall = await this.voice.connect(this.token, {
        params: {
          answerOnBridge: 'true',
          recipientType: 'client',
          to,
          from,
        },
      });

      this.activeCall.on(Call.Event.Connected, (res: any) => {
        this.setStatus(ECallStatus.Connected);
        console.log('Call connected', res);
      });
      this.activeCall.on(Call.Event.ConnectFailure, err => {
        this.setStatus(ECallStatus.ConnectFailure);
        console.log('Call ConnectFailure', err);
      });
      this.activeCall.on(Call.Event.Reconnecting, err => {
        this.setStatus(ECallStatus.Reconnecting);
        console.log('Call Reconnecting', err);
      });
      this.activeCall.on(Call.Event.Reconnected, () => {
        this.setStatus(ECallStatus.Reconnected);
        console.log('Call Reconnected');
      });
      this.activeCall.on(Call.Event.Disconnected, err => {
        this.setStatus(ECallStatus.Disconnected);
        console.log('Call Disconnected', err);
      });
      this.activeCall.on(Call.Event.Ringing, () => {
        this.setStatus(ECallStatus.Ringing);
        console.log('Call Ringing');
      });
      this.activeCall.on(Call.Event.QualityWarningsChanged, err => {
        console.log('Call QualityWarningsChanged', err);
      });
    } catch (error) {
      console.log('Error making call:', error);
    }
  }

  public async endCall(): Promise<boolean> {
    try {
      try {
        await this.activeCall?.disconnect();
      } catch (err) {
        // console.log('Call ended error', err);
      }
      this.setStatus(ECallStatus.Idle);
      this.activeCall = null;
      return true;
    } catch (error) {
      return false;
    }
  }

  public async resetCall() {
    try {
      try {
        await this.activeCall?.disconnect();
      } catch (error) {}
      try {
        await this.activeCall?.mute(false);
      } catch (error) {}

      this.activeCall = null;
      this.setStatus(ECallStatus.Idle);
    } catch (error) {
      // console.log('resetCall', error);
    }
  }

  public async toggleMuteMic(mute: boolean): Promise<boolean> {
    try {
      if (!this.activeCall) {
        console.log('No active call to toggleMuteMic');
        return false;
      }
      await this.activeCall.mute(mute);
      console.log('toggleMuteMic');
      return true;
    } catch (error) {
      console.log(`toggleMuteMic=${mute}`, error);
      return false;
    }
  }

  private setStatus(status: ECallStatus) {
    try {
      this.callStatus = status;
      this.notifyStatusSubscribers();
    } catch (error) {
      console.log('setStatus', error);
    }
  }

  private notifyStatusSubscribers() {
    try {
      this.statusSubscribers.forEach(callback => callback(this.callStatus));
    } catch (error) {
      console.log('notifyStatusSubscribers', error);
    }
  }

  public getStatus(): ECallStatus {
    try {
      return this.callStatus;
    } catch (error) {
      console.log('getStatus', error);
      return ECallStatus.ConnectFailure;
    }
  }

  public subscribeToStatusChanges(
    callback: (status: ECallStatus) => void,
  ): void {
    try {
      this.statusSubscribers.push(callback);
    } catch (error) {
      console.log('subscribeToStatusChanges', error);
    }
  }

  public unsubscribeToStatusChanges(
    callback: (status: ECallStatus) => void,
  ): void {
    try {
      this.statusSubscribers = this.statusSubscribers.filter(
        sub => sub !== callback,
      );
    } catch (error) {
      console.log('unsubscribeToStatusChanges', error);
    }
  }
}```
@hivelydev
Copy link

I can confirm this happen to our app as well!

@afalls-twilio
Copy link
Collaborator

afalls-twilio commented Oct 14, 2024

we are struggling to reproduce the issue, do you have any information on what action are occurring leading up to the crash?

We have a fix for it in the next release but without a means of reproducing the issue we can not verify. Please let us know if it resolves your issues.

@mudassir-iqbal
Copy link
Author

We can’t reproduce this issue on our end, but it’s affecting our customers, and crashes are being reported in Firebase Crashlytics.

@mhuynh5757
Copy link
Collaborator

Hey @mudassir-iqbal the fix is merged to main and we plan to cut a release this week. Thanks for your patience, we'll let you know when the next version is available. However, @afalls-twilio is correct in that we are unable to reproduce the issue. Please keep informed if you still encounter the issue. Thanks for helping us make the SDK better for everyone!

@mudassir-iqbal
Copy link
Author

Thanks for the update! I appreciate the hard work and will keep an eye out for any further issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants