2
votes

I'm building an Android music player that is controlled by messages received via BLE. My application is working fine on API 4.4(18) but it crashes in 8.1(27) possibly due to bad thread handling. Here is my Scan Activity and Music Player Activity as well as the errors from the LogCat

Scan Activity

public class MainActivity extends Activity {
private BluetoothAdapter mBluetoothAdapter;
private static final int REQUEST_ENABLE_BT = 1;
private static final long SCAN_PERIOD = 3000;
private Dialog mDialog;
public static final int permconst=7;
public static List<BluetoothDevice> mDevices = new ArrayList<BluetoothDevice>();
public static MainActivity instance = null;
private View mPermissionRationale;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.main);
    checkperms();

    if (!getPackageManager().hasSystemFeature(
            PackageManager.FEATURE_BLUETOOTH_LE)) {
        Toast.makeText(this, "Ble not supported", Toast.LENGTH_SHORT)
                .show();
        finish();
    }

    final BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = mBluetoothManager.getAdapter();
    if (mBluetoothAdapter == null) {
        Toast.makeText(this, "Ble not supported", Toast.LENGTH_SHORT)
                .show();
        finish();
        return;
    }

    if (!mBluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(
                BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }

    ImageButton btn = (ImageButton) findViewById(R.id.btn);
    btn.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            scanLeDevice();

            showRoundProcessDialog(MainActivity.this, R.layout.loading_process_dialog_anim);

            Timer mTimer = new Timer();
            mTimer.schedule(new TimerTask() {

                @Override
                public void run() {
                    Intent deviceListIntent = new Intent(getApplicationContext(),
                            Device.class);
                    startActivity(deviceListIntent);
                    mDialog.dismiss();
                }
            }, SCAN_PERIOD);
        }
    });

    //scanLeDevice();

    showRoundProcessDialog(MainActivity.this, R.layout.loading_process_dialog_anim);

    Timer mTimer = new Timer();
    mTimer.schedule(new TimerTask() {

        @Override
        public void run() {
            Intent deviceListIntent = new Intent(getApplicationContext(),
                    Device.class);
            startActivity(deviceListIntent);
            mDialog.dismiss();
        }
    }, SCAN_PERIOD);

    instance = this;
}

public void showRoundProcessDialog(Context mContext, int layout) {
    DialogInterface.OnKeyListener keyListener = new DialogInterface.OnKeyListener() {
        @Override
        public boolean onKey(DialogInterface dialog, int keyCode,
                             KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_HOME
                    || keyCode == KeyEvent.KEYCODE_SEARCH) {
                return true;
            }
            return false;
        }
    };

    mDialog = new AlertDialog.Builder(mContext).create();
    mDialog.setOnKeyListener(keyListener);
    mDialog.show();
    // 娉ㄦ��姝ゅ��瑕���惧��show涔���� ������浼���ュ��甯�
    mDialog.setContentView(layout);
}


private void scanLeDevice() {
    //checkperms();
   // new Thread() {
    final Handler handler =new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
           // if(Build.VERSION.SDK_INT>21){

           // mBluetoothAdapter.getBluetoothLeScanner().startScan(mleScanCallback);}    
            mBluetoothAdapter.startLeScan(mLeScanCallback);
                        }
    },4000);
    mBluetoothAdapter.stopLeScan(mLeScanCallback);
}

private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

    @Override
    public void onLeScan(final BluetoothDevice device, final int rssi,
                         byte[] scanRecord) {
        runOnUiThread(new Runnable() {
       // new Thread(){
            @Override
            public void run() {
                if (device != null) {

                    if (mDevices.indexOf(device) == -1)
                        mDevices.add(device);
                }
            }
        });
    }
};

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // User chose not to enable Bluetooth.
    if (requestCode == REQUEST_ENABLE_BT
            && resultCode == Activity.RESULT_CANCELED) {
        finish();
        return;
    }
    super.onActivityResult(requestCode, resultCode, data);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    //scanLeDevice(false);
    mDevices.clear();

    System.exit(0);
}

private void checkperms(){
    if(Build.VERSION.SDK_INT>19) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
            Toast.makeText(this, "permissions not granted", Toast.LENGTH_SHORT)
                    .show();
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_COARSE_LOCATION))
                Toast.makeText(this, "pls grant permissions", Toast.LENGTH_SHORT)
                        .show();
            else {
                // No explanation needed; request the permission
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},

                        permconst);
                return;}

        }}}}

Music Player:

public int[] bname=new int[]{R.drawable.playinv, R.drawable.pausex, R.drawable.pauseinv, R.drawable.playx,
        R.drawable.nextinv, R.drawable.nextx, R.drawable.previousinv, R.drawable.previousx, R.drawable.vdowninv,
        R.drawable.vdownx, R.drawable.vupinv, R.drawable.vupx};

private Map<UUID, BluetoothGattCharacteristic> map = new HashMap<UUID, BluetoothGattCharacteristic>();

private final ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName,
                                   IBinder service) {

        mBluetoothLeService = ((RBLService.LocalBinder) service)
                .getService();
        if (!mBluetoothLeService.initialize()) {
            Log.e(TAG, "Unable to initialize Bluetooth");
            finish();
        }
        // Automatically connects to the device upon successful start-up
        // initialization.
        final Handler handler =new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mBluetoothLeService.connect(mDeviceAddress);
            }
        },2000);

    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mBluetoothLeService = null;
    }
};

private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();

        if (RBLService.ACTION_GATT_DISCONNECTED.equals(action)) {
        } else if (RBLService.ACTION_GATT_SERVICES_DISCOVERED
                .equals(action)) {
            getGattService(mBluetoothLeService.getSupportedGattService());
        } else if (RBLService.ACTION_DATA_AVAILABLE.equals(action)) {
            displayData(intent.getByteArrayExtra(RBLService.EXTRA_DATA));
        }
    }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.music_player);
    checkperms();
    vup= findViewById(R.id.vup);
    vdown=findViewById(R.id.vdown);
    next =findViewById(R.id.next);
    previous= findViewById(R.id.previous);
    playpause =findViewById(R.id.plpau);
    info=findViewById(R.id.textView);
    tload=findViewById(R.id.load);
    audioManager=(AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
    Toast.makeText(getApplicationContext(),"Android Version: "+Build.VERSION.SDK_INT, Toast.LENGTH_LONG).show();
   // mediaPlayer = MediaPlayer.create(getApplicationContext(), tracks[current]);

    tload.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            getSongs();
            MUSIC_LOADED=1;
            info.setText(songT[current]);
            Uri trackUri = ContentUris.withAppendedId(
                    MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,songIDS[current]);
            mediaPlayer=MediaPlayer.create(getApplicationContext(), trackUri);
        }
    });
    playpause.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (pp) {
                mediaPlayer.start();
                playpause.setBackgroundResource(R.drawable.pausex);
                pp = FALSE;
            } else {
                mediaPlayer.pause();
                playpause.setBackgroundResource(R.drawable.playx);
                pp = TRUE;}
        }});

    next.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
           nextTrack();
        } });
    previous.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            previousTrack();
        } });
    vdown.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            volumeDown(audioManager);
        }});
    vup.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            volumeUp(audioManager);            }});

    Intent intent = getIntent();
    mDeviceAddress = intent.getStringExtra(Device.EXTRA_DEVICE_ADDRESS);
    mDeviceName = intent.getStringExtra(Device.EXTRA_DEVICE_NAME);
    //        getActionBar().setTitle(mDeviceName);
    //      getActionBar().setDisplayHomeAsUpEnabled(true);
    Intent gattServiceIntent = new Intent(this, RBLService.class);
    bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onResume() {
    super.onResume();

    registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
    //mediaPlayer.stop();
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == android.R.id.home) {
        mBluetoothLeService.disconnect();
        mBluetoothLeService.close();
       // mediaPlayer.stop();
        System.exit(0);
    }

    return super.onOptionsItemSelected(item);
}

@Override
protected void onStop() {
    super.onStop();

    unregisterReceiver(mGattUpdateReceiver);
    //mediaPlayer.stop();

}

@Override
protected void onDestroy() {
    super.onDestroy();

    mBluetoothLeService.disconnect();
    mBluetoothLeService.close();
    //mediaPlayer.stop();
    System.exit(8);
}

private void displayData(byte[] byteArray) {
    if (MUSIC_LOADED==1){
        if (byteArray != null) {
        String data = new String(byteArray);
      x= data.substring (data.length()-1);
        //Toast.makeText(getApplicationContext(),x,Toast.LENGTH_SHORT).show();
        if (x.equals("1")){
            selector=10;
            colorChange(vup,selector);
            Toast.makeText(getApplicationContext(),"Infitex_keypress_001"+"\n"+ "Volume up",Toast.LENGTH_SHORT).show();
            volumeUp(audioManager);
        }
        if (x.equals("3")){
            selector=4;
            colorChange(next,selector);
            Toast.makeText(getApplicationContext(),"Infitex_keypress_003"+"\n"+ "Track Advance",Toast.LENGTH_SHORT).show();
            nextTrack();
        }
        if (x.equals("5") ){
            if (pp){
            selector=0;} else selector= 2;
            colorChange(playpause,selector);
            Toast.makeText(getApplicationContext(),"Infitex_keypress_005"+"\n"+ "Play/Pause",Toast.LENGTH_SHORT).show();
            playPause(playpause);}

     if(x.equals("7")){
         selector=6;
         colorChange(previous,selector);
         Toast.makeText(getApplicationContext(),"Infitex_keypress_007"+"\n"+ "Track Reverse",Toast.LENGTH_SHORT).show();
         previousTrack();
        }
     if(x.equals("9")){
         selector=8;
         colorChange(vdown,selector);
         Toast.makeText(getApplicationContext(),"Infitex_keypress_009"+"\n"+ "Volume Down",Toast.LENGTH_SHORT).show();
         volumeDown(audioManager);
        }
    }}}

private void getGattService(BluetoothGattService gattService) {
    if (gattService == null)
        return;

    BluetoothGattCharacteristic characteristic = gattService
            .getCharacteristic(RBLService.UUID_BLE_SHIELD_TX);
    map.put(characteristic.getUuid(), characteristic);

    BluetoothGattCharacteristic characteristicRx = gattService
            .getCharacteristic(RBLService.UUID_BLE_SHIELD_RX);
    mBluetoothLeService.setCharacteristicNotification(characteristicRx,
            true);
    mBluetoothLeService.readCharacteristic(characteristicRx);
}

private static IntentFilter makeGattUpdateIntentFilter() {
    final IntentFilter intentFilter = new IntentFilter();

    intentFilter.addAction(RBLService.ACTION_GATT_CONNECTED);
    intentFilter.addAction(RBLService.ACTION_GATT_DISCONNECTED);
    intentFilter.addAction(RBLService.ACTION_GATT_SERVICES_DISCOVERED);
    intentFilter.addAction(RBLService.ACTION_DATA_AVAILABLE);
    return intentFilter;
}

void nextTrack(){
    mediaPlayer.pause();
    current+=1;
    if (current==5){
        current=0;
    }

    getURI();
    info.setText(songT[current]);
    playpause.setBackgroundResource(R.drawable.pausex);
    mediaPlayer.start();
    pp=FALSE;

}
void previousTrack(){
    mediaPlayer.pause();
    if (current==0){
        current=4;}
    else {
        current -= 1;
    }
    getURI();

    info.setText(songT[current]);
    playpause.setBackgroundResource(R.drawable.pausex);
    mediaPlayer.start();
    pp=FALSE;
}
public void playPause(ImageButton imageButton){
    if (pp) {
        mediaPlayer.start();
        imageButton.setBackgroundResource(R.drawable.pausex);
        pp = FALSE;
    } else if(pp==FALSE) {
        mediaPlayer.pause();
        imageButton.setBackgroundResource(R.drawable.playx);
        pp = TRUE;
    }

}
public void volumeDown(AudioManager audioManager){
    audioManager.adjustVolume(AudioManager.ADJUST_LOWER,AudioManager.FLAG_PLAY_SOUND);
}
public void volumeUp(AudioManager audioManager){
    audioManager.adjustVolume(AudioManager.ADJUST_RAISE,AudioManager.FLAG_PLAY_SOUND);
}
public void colorChange(final ImageButton imageButton, final int name){
        final int last= name+1;
    new CountDownTimer(1500, 1000) {
        public void onTick(long millisUntilFinished) {
            imageButton.setBackgroundResource(bname[name]);
        }
        public void onFinish() {
            imageButton.setBackgroundResource(bname[last]);
        }
    }.start();
}
    //  public void bCheck(ImageButton imageButton){
    public void getSongs(){
    int i=0;
    ContentResolver contentResolver=getContentResolver();
    Uri uri= EXTERNAL_CONTENT_URI;
    Cursor songcursor= contentResolver.query(uri,null,null,null,null);

    if (songcursor!=null && songcursor.moveToFirst()) {
    int songtitle=songcursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
    int songID=songcursor.getColumnIndex(MediaStore.Audio.Media._ID);

    do{
        songT[i] = songcursor.getString(songtitle);
        songIDS[i]=songcursor.getLong(songID);
        i+=1;
    }
    while(songcursor.moveToNext());
    songcursor.close();

}}
public void getURI(){
    Uri trackUri = ContentUris.withAppendedId(
            android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,songIDS[current]);
    mediaPlayer=MediaPlayer.create(getApplicationContext(), trackUri);
}
private void checkperms(){
    //Toast.makeText(this,"version", Toast.LENGTH_LONG);
    if(Build.VERSION.SDK_INT>19) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
            Toast.makeText(getApplicationContext(), "permissions not granted", Toast.LENGTH_SHORT)
                    .show();
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.READ_EXTERNAL_STORAGE))
                Toast.makeText(getApplicationContext(),"pls grant permissions", Toast.LENGTH_SHORT)
                        .show();
            else {
                // No explanation needed; request the permission
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                        MainActivity.permconst);
                }

        }}}

}

And the logcat:

com.example.parlatas.messages I/AndroidRuntime: VM exiting with result code 0, cleanup skipped.

I/WindowManager: WIN DEATH: Window{9297a52 u0 com.example.parlatas.messages/com.example.parlatas.messages.MainActivity} I/ActivityManager: Process com.example.parlatas.messages (pid 8530) has died: fore TOP W/ActivityManager: Force removing ActivityRecord{b15576d u0 com.example.parlatas.messages/.MusicPlayer t245}: app died, no saved state W/InputDispatcher: channel 'd7b3552 com.example.parlatas.messages/com.example.parlatas.messages.MusicPlayer (server)' ~ Consumer closed input channel or an error occurred. events=0x9 E/InputDispatcher: channel 'd7b3552 com.example.parlatas.messages/com.example.parlatas.messages.MusicPlayer (server)' ~ Channel is unrecoverably broken and will be disposed! I/WindowManager: WIN DEATH: Window{d7b3552 u0 com.example.parlatas.messages/com.example.parlatas.messages.MusicPlayer} W/InputDispatcher: Attempted to unregister already unregistered input channel 'd7b3552 com.example.parlatas.messages/com.example.parlatas.messages.MusicPlayer (server)' W/NotificationService: Object died trying to hide notification android.app.ITransientNotification$Stub$Proxy@c10b00c in package com.example.parlatas.messages

I have separate classes with the BLE constants and the Bluetooth Device List that gets populated by the Scan Activity (Main Activity)

2

2 Answers

0
votes

Try adding the below permissions in the manifest if you are targeting higher API level than 22.New version require Coarse location permission(accessing cellular location information) if you are to use Bluetooth hardware. It doesn't make sense but It required in New API levels.

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-feature android:name="android.hardware.bluetooth_le"

Then either you have to give the permission or should target a low API level.

0
votes

Did you try to get runtime permission?

https://developer.android.com/reference/java/lang/Runtime

Look at the link. You can take runtime permission leave and solve the problem.