Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

Dealing with Android M permissions

I need my app to work on both Android M (and later) as well as pre M versions.

My app requires multiple permissions but thus far I’ve only been able to get it to request the last permission in the list. I tried to follow this guide here…

http://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en

The code below is the closest I can get, It prompts for one permission on Android M but it still run on pre M builds where as more closely matching the guide above it prompts for one permission on M and crashes on pre-M devices.

Can anyone help me flesh this out so I can trudge forward on this development?

\AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest package="testing.com.rtp_test"
          xmlns:android="http://schemas.android.com/apk/res/android">


    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

\build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "testing.com.rtp_test"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.1.1'
}

\MainActivity.java

package testing.com.rtp_test;

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.net.rtp.AudioCodec;
import android.net.rtp.AudioGroup;
import android.net.rtp.AudioStream;
import android.net.rtp.RtpStream;
import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.widget.Toast;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.Manifest;

public class MainActivity extends Activity {

    private static final String TAG = MainActivity.class.getSimpleName();


    private static final int REQUEST_INTERNET_RESULT = 0;
    private static final int REQUEST_MODIFY_AUDIO_SETTINGS_RESULT = 1;
    private static final int REQUEST_RECORD_AUDIO_RESULT = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED
                  ) {
                    AudioManager audio =  (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                    audio.setMode(AudioManager.MODE_IN_COMMUNICATION);
                    AudioGroup audioGroup = new AudioGroup();
                    audioGroup.setMode(AudioGroup.MODE_NORMAL);
                    AudioStream audioStream = new AudioStream(InetAddress.getByAddress(getLocalIPAddress()));
                    audioStream.setCodec(AudioCodec.PCMA);
                    audioStream.setMode(RtpStream.MODE_NORMAL);
                    //set receiver(vlc player) machine ip address(please update with your machine ip)

                    audioStream.associate(InetAddress.getByAddress(new byte[] {(byte)172, (byte)26, (byte)15, (byte)89 }), 8888);
                    audioStream.join(audioGroup);
                } else {
                    if(shouldShowRequestPermissionRationale(android.Manifest.permission.INTERNET)) {
                        Toast.makeText(this, "Need access to the internet to stream", Toast.LENGTH_LONG).show();
                    }
                    if(shouldShowRequestPermissionRationale(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)) {
                        Toast.makeText(this, "Need access to audio settings to stream", Toast.LENGTH_LONG).show();
                    }
                    if(shouldShowRequestPermissionRationale(android.Manifest.permission.RECORD_AUDIO)) {
                        Toast.makeText(this, "Need access to record to stream", Toast.LENGTH_LONG).show();
                    }


                    requestPermissions(new String[] {android.Manifest.permission.INTERNET},
                            REQUEST_INTERNET_RESULT);
                    requestPermissions(new String[] {android.Manifest.permission.MODIFY_AUDIO_SETTINGS},
                            REQUEST_MODIFY_AUDIO_SETTINGS_RESULT);
                    requestPermissions(new String[] {android.Manifest.permission.RECORD_AUDIO},
                            REQUEST_RECORD_AUDIO_RESULT);
                }
            } else {
                AudioManager audio =  (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                audio.setMode(AudioManager.MODE_IN_COMMUNICATION);
                AudioGroup audioGroup = new AudioGroup();
                audioGroup.setMode(AudioGroup.MODE_NORMAL);
                AudioStream audioStream = new AudioStream(InetAddress.getByAddress(getLocalIPAddress()));
                audioStream.setCodec(AudioCodec.PCMA);
                audioStream.setMode(RtpStream.MODE_NORMAL);
                //set receiver(vlc player) machine ip address(please update with your machine ip)

                audioStream.associate(InetAddress.getByAddress(new byte[] {(byte)172, (byte)26, (byte)15, (byte)89 }), 8888);
                audioStream.join(audioGroup);
            }


        } catch (Exception e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if(requestCode == REQUEST_INTERNET_RESULT && requestCode == REQUEST_MODIFY_AUDIO_SETTINGS_RESULT && requestCode == REQUEST_RECORD_AUDIO_RESULT) {
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                AudioManager audio =  (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                audio.setMode(AudioManager.MODE_IN_COMMUNICATION);
                AudioGroup audioGroup = new AudioGroup();
                audioGroup.setMode(AudioGroup.MODE_NORMAL);
                AudioStream audioStream = null;
                try {
                    audioStream = new AudioStream(InetAddress.getByAddress(getLocalIPAddress()));
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                audioStream.setCodec(AudioCodec.PCMA);
                audioStream.setMode(RtpStream.MODE_NORMAL);
                //set receiver(vlc player) machine ip address(please update with your machine ip)

                try {
                    audioStream.associate(InetAddress.getByAddress(new byte[] {(byte)192, (byte)168, (byte)0, (byte)100 }), 8888);
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                audioStream.join(audioGroup);
            } else {
                Toast.makeText(this, "Without sufficient permissions we cannot start a stream.", Toast.LENGTH_LONG).show();
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }

    }

    public static byte[] getLocalIPAddress () {
        byte ip[]=null;
        try {
            for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = (NetworkInterface) en.nextElement();
                for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = (InetAddress) enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        ip= inetAddress.getAddress();
                    }
                }
            }
        } catch (SocketException ex) {
            Log.i("SocketException ", ex.toString());
        }
        return ip;

    }
}

1 Answer

Ben Deitch
STAFF
Ben Deitch
Treehouse Teacher

You shouldn't need to request the INTERNET or MODIFY_AUDIO_SETTINGS permissions. They're both normal permissions.

Well this is extra weird then because my code works on pre-M devices but not M devices...

Thanks for the link to Normal Permissions I wasn't sure what normal permissions meant in the few articles I saw it mentioned.

Looking forward to more content on the subject here at Treehouse. I've learned all that is offered but I still don't think I'm ready

I did notice this make various activities in this case (MainActivity) very bloated. I'd like to see this...

http://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en

implimented in its own file Permissions.java or something have main activity call to this to check the permissions have permissions do its thing and then continue.