javaandroidandroid-9.0-pieandroid-securityexception

My app keeps crashing in android 9 version throwing java.lang.SecurityException


I recently developed an android app that streams radio online using a URL. It has been working well in other android versions that is 6,7and 8 the ones i tested in emulators.

But last week i published the app to play store, and my reports show that it crashes in android 9 phones. It keeps on throwing java.lang.SecurityException. I have tried what i could to resolve the error but i have failed. The users keep reporting multiple app crashes on their phones

This is the stack trace from the play console

java.lang.RuntimeException: 
  at android.app.ActivityThread.handleServiceArgs (ActivityThread.java:3903)
  at android.app.ActivityThread.access$1700 (ActivityThread.java:236)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1815)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:214)
  at android.app.ActivityThread.main (ActivityThread.java:7032)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)
Caused by: java.lang.SecurityException: 
  at android.os.Parcel.createException (Parcel.java:1966)
  at android.os.Parcel.readException (Parcel.java:1934)
  at android.os.Parcel.readException (Parcel.java:1884)
  at android.app.IActivityManager$Stub$Proxy.setServiceForeground (IActivityManager.java:5043)
  at android.app.Service.startForeground (Service.java:695)
  at com.premar.radiomunabuddu.RadioMediaPlayerService.play (RadioMediaPlayerService.java:120)
  at com.premar.radiomunabuddu.RadioMediaPlayerService.onStartCommand (RadioMediaPlayerService.java:50)
  at android.app.ActivityThread.handleServiceArgs (ActivityThread.java:3884)
Caused by: android.os.RemoteException: 
  at com.android.server.am.ActivityManagerService.enforcePermission (ActivityManagerService.java:12159)
  at com.android.server.am.ActiveServices.setServiceForegroundInnerLocked (ActiveServices.java:1289)
  at com.android.server.am.ActiveServices.setServiceForegroundLocked (ActiveServices.java:969)
  at com.android.server.am.ActivityManagerService.setServiceForeground (ActivityManagerService.java:24839)
  at android.app.IActivityManager$Stub.onTransact$setServiceForeground$ (IActivityManager.java:11378)

This is the HomeActivity.java

public class HomeActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {
    FancyButton listenRadio;
    ImageView facebook, twitter, instagram, linkedin;
    RadioSettings settings;
    Context context;
    public static final int REQUEST_CODE =123;

    private Button stopButton = null;
    private Button playButton = null;
    private Button phoneCall;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        ButterKnife.bind(this);
        settings = new RadioSettings();

        //views
        phoneCall = (Button)this.findViewById(R.id.phoneBtn);


        //Allow hardware audio buttons to control volume
        setVolumeControlStream(AudioManager.STREAM_MUSIC);

        clickListeners(); //Start click listeners

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

    }

    private void openFacebookProfile() {
        try {
            String facebookURL = getFacebookPageUrl();
            Intent facebookIntent = new Intent(Intent.ACTION_VIEW);
            facebookIntent.setData(Uri.parse(facebookURL));
            startActivity(facebookIntent);
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    private String getFacebookPageUrl() {
        final String facebookUrl = settings.getFacebookAddress();
        String fbURL = null;
        PackageManager packageManager = getPackageManager();
        try {
            if (packageManager != null){
                Intent fbIntent = packageManager.getLaunchIntentForPackage("com.facebook.katana");
                if (fbIntent != null){
                    int versionCode = packageManager.getPackageInfo("com.facebook.katana",0).versionCode;
                    if (versionCode >= 3002850){
                        fbURL = "fb://page/1993598950880589";
                    }
                } else {
                    fbURL = facebookUrl;
                }
            }
            else {
                fbURL = facebookUrl;
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            fbURL = facebookUrl;
        }
        return fbURL;
    }

    private void openTwitterProfile(){
        Intent intent = null;
        try {
            this.getPackageManager().getPackageInfo("com.twitter.android", 0);
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse("twitter://user?user_id=USERID"));
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        } catch (Exception e){
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://twitter.com/profilename"));
        }
        this.startActivity(intent);
    }


    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.home, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {

            case R.id.watch_webcam: {
                launchWebcam();
                break;
            }

            case R.id.playstore_share: {
                /*
                Uri uri = Uri.parse("market://details?id=" + context.getPackageName());
                Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri);
                // To count with Play market backstack, After pressing back button,
                // to taken back to our application, we need to add following flags to intent.
                goToMarket.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY |
                        Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
                        Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
                try {
                    startActivity(goToMarket);
                } catch (ActivityNotFoundException e) {
                    startActivity(new Intent(Intent.ACTION_VIEW,
                            Uri.parse("http://play.google.com/store/apps/details?id=" + context.getPackageName())));
                }
                break;
                */
            }

        }

        return super.onOptionsItemSelected(item);
    }


    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

         if (id == R.id.nav_share) {
            Intent shareIntent = new Intent(Intent.ACTION_SEND);
            shareIntent.setType("text/plain");
             String shareMessage= "\nPlease download our Radiomunnabuddu USA app from the Play Store\n\n";
             shareMessage = shareMessage + "https://play.google.com/store/apps/details?id=" + BuildConfig.APPLICATION_ID +"\n\n";
            shareIntent.putExtra(Intent.EXTRA_TEXT  , shareMessage);
            shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Radio Munnabuddu USA");
            startActivity(Intent.createChooser(shareIntent, "Share via..."));

        }
        else if (id == R.id.nav_email){
            Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
            emailIntent.setData(Uri.parse("mailto: "+settings.getEmailAddress()));
            emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Radio Munnabuddu USA");
            if (emailIntent.resolveActivity(getPackageManager()) != null){
                startActivity(Intent.createChooser(emailIntent, "Send email via"));
            }
        }
        else if(id == R.id.nav_report){
            Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
            emailIntent.setData(Uri.parse("mailto: denis@premar.tech"));
            emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Crash or Bug report");
             if (emailIntent.resolveActivity(getPackageManager()) != null){
                 startActivity(Intent.createChooser(emailIntent, "Send email via."));
             }
         }
        else if(id == R.id.nav_about){
            Intent aboutIntent = new Intent(HomeActivity.this, AboutActivity.class);
            startActivity(aboutIntent);
        }
        else if(id == R.id.nav_fb){
            openFacebookProfile();
        }
        else if(id == R.id.nav_twitter){
          openTwitterProfile();
        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;

    }


    /**
     * Listens for contact button clicks
     */
    private void clickListeners(){
        //Play button
        playButton = (Button)findViewById(R.id.PlayButton);
        playButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(),
                        RadioMediaPlayerService.class);
                intent.putExtra(RadioMediaPlayerService.START_PLAY, true);
                startService(intent);

            }
        });

        //Stop button
        stopButton = (Button)findViewById(R.id.StopButton);
        stopButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                //Get new MediaPlayerService activity
                Intent intent = new Intent(getApplicationContext(),
                        RadioMediaPlayerService.class);
                stopService(intent);
            }
        });

        //Email Button click list
        final View EmailPress = (Button)this.findViewById(R.id.emailBtn);
        EmailPress.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view){

                Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
                emailIntent.setData(Uri.parse("mailto: "+settings.getEmailAddress()));
                emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Radio Munnabuddu");
                if (emailIntent.resolveActivity(getPackageManager()) != null){
                    try {
                        startActivity(Intent.createChooser(emailIntent, "Send email..."));
                    } catch (android.content.ActivityNotFoundException ex) {
                        Toast.makeText(HomeActivity.this, "There are no email clients installed.", Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });



        //Website Button
        final View WWWPress = (Button)this.findViewById(R.id.websiteBtn);
        WWWPress.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view){

                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(settings.getWebsiteURL())); //URL
                startActivity (browserIntent);

            }
        });

        //SMS Button
        final View TxtPress = (Button)this.findViewById(R.id.txtBtn);
        TxtPress.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view){

                Uri uri = Uri.parse(settings.getSmsNumber());
                Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
                intent.putExtra("sms_body", "Hello Presenter,");
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(intent);
                }
                /*
                if (ActivityCompat.checkSelfPermission(HomeActivity.this, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED){
                    Toast.makeText(HomeActivity.this, "Please grant the permission to call", Toast.LENGTH_SHORT).show();
                    requestSMSPermission();
                }
                else {
                    startActivity (smsIntent);
                }*/

            }
        });
    }


    /**
     * Launches webcam from external URL
     */
    public void launchWebcam(){
        Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(settings.getRadioWebcamURL()));
        startActivity (browserIntent);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {

            case REQUEST_CODE:
                if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    onCall();
                } else {
                    Log.d("TAG", "Call Permission Not Granted");
                    //Toast.makeText(this, "Call Permission Not Granted", Toast.LENGTH_SHORT).show();
                }
                return;

            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    public void onCall() {
        int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE);

        if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                    this,
                    new String[]{Manifest.permission.CALL_PHONE},
                    REQUEST_CODE);
        } else {
            phoneCall.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view){
                    /*
                    String phoneNum = settings.getPhoneNumber();
                    Intent phoneIntent = new Intent(Intent.ACTION_CALL);
                    phoneIntent.setData(Uri.parse("tel:"+ phoneNum));
                    if (phoneIntent.resolveActivity(getPackageManager()) != null) {
                        startActivity(phoneIntent);
                    }
                    */
                    startActivity(new Intent(Intent.ACTION_CALL).setData(Uri.parse("tel:" + settings.getPhoneNumber())));
                }
            });

        }
    }

This is the RadioMediaPlayerService.java

public class RadioMediaPlayerService extends Service implements
        AudioManager.OnAudioFocusChangeListener {
    //Variables
    private boolean isPlaying = false;
    private MediaPlayer radioPlayer; //The media player instance
    private static int classID = 579; // just a number
    public static String START_PLAY = "START_PLAY";
    AudioManager audioManager;
    //Media session
    MediaSession mSession;

    //Settings
    RadioSettings settings = new RadioSettings();


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    /**
     * Starts the streaming service
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent.getBooleanExtra(START_PLAY, false)) {

            play();
        }
        //Request audio focus
        if (!requestAudioFocus()) {
            //Could not gain focus
            stopSelf();
        }
        return Service.START_STICKY;
    }

    @Override
    public void onCreate() {

        super.onCreate();
    }

    /**
     * Starts radio URL stream
     */
    private void play() {

        //Check connectivity status
        if (isOnline()) {
            //Check if player already streaming
            if (!isPlaying) {
                isPlaying = true;

                //Return to the current activity
                Intent intent = new Intent(this, HomeActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|
                        Intent.FLAG_ACTIVITY_SINGLE_TOP);

                PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//                mSession.setSessionActivity(pi);

                //Build and show notification for radio playing
                Bitmap largeIcon = BitmapFactory.decodeResource(getResources(),
                        R.drawable.buddu3);
                Notification notification = new NotificationCompat.Builder(this, "ID")
                        .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                        .setTicker("Radio Munnabuddu USA")
                        .setContentTitle(settings.getRadioName())
                        .setContentText(settings.getMainNotificationMessage())
                        .setSmallIcon(R.drawable.ic_radio_black_24dp)
                        //.addAction(R.drawable.ic_play_arrow_white_64dp, "Play", pi)
                       // .addAction(R.drawable.ic_pause_black_24dp, "Pause", pi)
                        .setLargeIcon(largeIcon)
                        .setContentIntent(pi)
                        .build();

                //Get stream URL
                radioPlayer = new MediaPlayer();
                try {
                    radioPlayer.setDataSource(settings.getRadioStreamURL()); //Place URL here
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalStateException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                radioPlayer.prepareAsync();
                radioPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

                    public void onPrepared(MediaPlayer mp) {
                        radioPlayer.start(); //Start radio stream
                    }
                });

                startForeground(classID, notification);

                //Display toast notification
                Toast.makeText(getApplicationContext(), settings.getPlayNotificationMessage(),
                        Toast.LENGTH_LONG).show();
            }
        }
        else {
            //Display no connectivity warning
            Toast.makeText(getApplicationContext(), "No internet connection",
                    Toast.LENGTH_LONG).show();
        }


    }


    /**
     * Stops the stream if activity destroyed
     */
    @Override
    public void onDestroy() {
        stop();
        removeAudioFocus();
    }

    /**
     * Stops audio from the active service
     */
    private void stop() {
        if (isPlaying) {
            isPlaying = false;
            if (radioPlayer != null) {
                radioPlayer.release();
                radioPlayer = null;
            }
            stopForeground(true);
        }

        Toast.makeText(getApplicationContext(), "Radio stopped",
                Toast.LENGTH_LONG).show();
    }


    /**
     * Checks if there is a data or internet connection before starting the stream.
     * Displays Toast warning if there is no connection
     * @return online status boolean
     */
    public boolean isOnline() {
        ConnectivityManager cm =
                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo netInfo = cm.getActiveNetworkInfo();
        if (netInfo != null && netInfo.isConnectedOrConnecting()) {
            return true;
        }
        return false;
    }

    @Override
    public void onAudioFocusChange(int focusChange) {

        //Invoked when the audio focus of the system is updated.
        switch (focusChange) {
            /*
            case AudioManager.AUDIOFOCUS_GAIN:
                // resume playback
                // if (radioPlayer == null) initMediaPlayer();
                if (radioPlayer.isPlaying()){
                    radioPlayer.release();
                    stopForeground(true);
                }

                radioPlayer.setVolume(1.0f, 1.0f);
                break;*/
            case AudioManager.AUDIOFOCUS_LOSS:
                // Lost focus for an unbounded amount of time: stop playback and release media player
                if (radioPlayer.isPlaying()) radioPlayer.stop();
                radioPlayer.release();
                //radioPlayer = null;
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                // Lost focus for a short time, but we have to stop
                // playback. We don't release the media player because playback
                // is likely to resume
                if (radioPlayer.isPlaying()) radioPlayer.pause();
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                // Lost focus for a short time, but it's ok to keep playing
                // at an attenuated level
                if (radioPlayer.isPlaying()) radioPlayer.setVolume(0.1f, 0.1f);
                break;
        }
    }

    /**
     * AudioFocus
     */
    private boolean requestAudioFocus() {
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            //Focus gained
            return true;
        }
        //Could not gain focus
        return false;
    }

    private boolean removeAudioFocus() {
        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
                audioManager.abandonAudioFocus(this);
    }


}

This is the Manifest.xml

 <!--uses-permission android:name="android.permission.SEND_SMS" /-->
    <uses-permission android:name="android.permission.INTERNET" />
    <!--uses-permission android:name="android.permission.CALL_PHONE" /-->
    <!--tools:node="remove"-->


    <uses-feature
        android:name="android.hardware.telephony"
        android:required="false" />

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <service
            android:name="com.premar.radiomunabuddu.RadioMediaPlayerService"
            android:enabled="true" >

        </service>

        <receiver android:name="com.premar.radiomunabuddu.IntentReceiver">
            <intent-filter>
                <action android:name="android.media.AUDIO_BECOMING_NOISY" />
            </intent-filter>
        </receiver>

        <activity
            android:name="com.premar.radiomunabuddu.HomeActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.premar.radiomunabuddu.AboutActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.premar.radiomunabuddu.HomeActivity" />
        </activity>
    </application>

Solution

  • Android 9 introduced a new FOREGROUND_SERVICE permission. From the docs:

    Note: Apps that target Android 9 (API level 28) or higher and use foreground services must request the FOREGROUND_SERVICE permission. This is a normal permission, so the system automatically grants it to the requesting app.

    If an app that targets API level 28 or higher attempts to create a foreground service without requesting FOREGROUND_SERVICE, the system throws a SecurityException.

    Just add that permission to your manifest and you should be good to go.